霍夫曼编码(贪心算法)

1.背景

  • 最近看到一个贪心算法的概念,比较多的举例是用各种面额的钞票给人快速找零;突然一个霍夫曼编码的名词出现,也是用贪心算法,有什么用呢,问 ai 说是一种压缩的概念,勾起了我的好奇心,这是干啥的,怎么实现的。

2.图说原理

在这里插入图片描述

2.1 为啥要编码

  • 首先算出频率,就是每个字出现的次数,然后编码成 0101011010100... 类似这样的格式,为什么呢,因为 01可以按存储,正常的一个字符比如采用utf8mb4是4个字节,一个字节是8位,那么一个字符就占用32 位了,裸眼计算明显按位存储更优;如上图所示,但是长度、可行性不一样

2.2 二进制编码

  • 去重后的数量来计算二进制位数,比如 3 个字,那么用 2 位(4种可能)表示,7 个字用 3 位(8种可能)表示,12 位用 4 位(16种可能);每一个字符都有固定的长度和唯一的编码;可以对存储的编码进行解码,但是最长

2.3 简单编码

  • 直接用最短的位数开始排,这样最少的位数能表示最多的编码;明显最短,但是解码是一个头疼的问题。对应 01怎么区分是“01是两个字”,还是“01一个字三”,别看我图上有空格,但是真正按位存储的时候是没有空格的。

2.4 霍夫曼编码

  • 使用霍夫曼编码生成的编码,每一个字的对应的前缀是唯一的,没有像第二种那样重复的情况,直接从头匹配,找到了就翻译,去掉翻译完的位,接着往下找就可以了;不最长不最短,但是能解码;解码时候比第一种固定长度的麻烦,但是是可以实现的,压缩嘛,压缩率的权重可以看的更高撒;(个人不成熟认为固定长度编码方便并发解码,毕竟可以等位分割起线程;让我来搞霍夫曼编码可能会加入特殊的字节用来分割它,分割后进行并发处理;有点飘了,实际的压缩算法看 AI 介绍比单纯一种算法更麻烦,会对霍夫曼编码优化或者变形,不想深入了)

3.代码实现霍夫曼编码(AI 写的)

import heapq


class Node:
    def __init__(self, char, freq):
        self.char = char
        self.freq = freq
        self.left = None
        self.right = None

    def __lt__(self, other):
        return self.freq < other.freq


def build_huffman_tree(chars, freqs):
    """构建霍夫曼树"""
    heap = []
    for char, freq in zip(chars, freqs):
        heapq.heappush(heap, Node(char, freq))

    while len(heap) > 1:
        left = heapq.heappop(heap)
        right = heapq.heappop(heap)

        merged = Node(None, left.freq + right.freq)
        merged.left = left
        merged.right = right

        heapq.heappush(heap, merged)

    return heap[0]


def generate_codes(node, current_code, codes):
    """生成霍夫曼编码"""
    if node is None:
        return

    if node.char is not None:
        codes[node.char] = current_code
        return

    generate_codes(node.left, current_code + "0", codes)
    generate_codes(node.right, current_code + "1", codes)


def huffman_coding(chars, freqs):
    """霍夫曼编码主函数"""
    root = build_huffman_tree(chars, freqs)
    print('root',root)
    codes = {}
    generate_codes(root, "", codes)
    return codes


# 测试
chars = ['我', '是', '张', '三', '丰',',','无','忌','的','师','傅']
freqs = [2,2,2,1,1,1,1,1,1,1,1]
huffman_codes = huffman_coding(chars, freqs)
print("霍夫曼编码:")
for char, code in huffman_codes.items():
    print(f"{char}: {code}")
  • 算法就是先生成一颗二叉树,然后对这棵树从树根到叶子编码,左0右1.树结构如下:
    在这里插入图片描述

4.优化

  • 上面的图在执行的时候使用 python 的 heap 进行插入的时候,heap 并没有按照插入顺序保存,这可能是 heap 的什么机制吧,如下图
    在这里插入图片描述

  • 还有一个优化的点,就是先把频率按照词频的正序排列后再编码,因为根据算法越到后面入队的话,生成的编码相对较短,越短的位来存储较高频率的字符,占用的空间就越少

  • 最后给霍夫曼点个 6

霍夫曼编码是一种常用的数据压缩算法,它通过将出现频率较高的字符用较短的编码表示,从而实现对数据的高效压缩。贪心算法霍夫曼编码的核心思想之一。 在C++中实现霍夫曼编码贪心算法,可以按照以下步骤进行: 1. 创建一个结构体或类来表示霍夫曼树的节点,包含字符、字符出现的频率以及左右子节点等信息。 2. 统计给定文本中每个字符出现的频率,并根据频率构建一个最小堆(优先队列),以频率作为比较的依据。 3. 从最小堆中选择两个频率最低的节点,将它们合并为一个新节点,并将新节点插入到最小堆中。 4. 重复步骤3,直到最小堆中只剩下一个节点,即霍夫曼树的根节点。 5. 遍历霍夫曼树,给每个字符赋予对应的编码。左子节点为0,右子节点为1。 6. 使用生成的霍夫曼编码对原始文本进行压缩。 下面是一个简单的C++代码示例,实现了霍夫曼编码贪心算法: ```cpp #include <iostream> #include <queue> #include <unordered_map> using namespace std; struct HuffmanNode { char data; int freq; HuffmanNode* left; HuffmanNode* right; }; struct Compare { bool operator()(HuffmanNode* a, HuffmanNode* b) { return a->freq > b->freq; } }; HuffmanNode* buildHuffmanTree(unordered_map<char, int>& freqMap) { priority_queue<HuffmanNode*, vector<HuffmanNode*>, Compare> minHeap; for (auto& pair : freqMap) { HuffmanNode* node = new HuffmanNode(); node->data = pair.first; node->freq = pair.second; node->left = nullptr; node->right = nullptr; minHeap.push(node); } while (minHeap.size() > 1) { HuffmanNode* left = minHeap.top(); minHeap.pop(); HuffmanNode* right = minHeap.top(); minHeap.pop(); HuffmanNode* newNode = new HuffmanNode(); newNode->data = '\0'; newNode->freq = left->freq + right->freq; newNode->left = left; newNode->right = right; minHeap.push(newNode); } return minHeap.top(); } void generateHuffmanCodes(HuffmanNode* root, string code, unordered_map<char, string>& codes) { if (root == nullptr) { return; } if (root->left == nullptr && root->right == nullptr) { codes[root->data] = code; } generateHuffmanCodes(root->left, code + "0", codes); generateHuffmanCodes(root->right, code + "1", codes); } string compressText(string text, unordered_map<char, string>& codes) { string compressedText = ""; for (char c : text) { compressedText += codes[c]; } return compressedText; } int main() { string text = "Hello, World!"; unordered_map<char, int> freqMap; for (char c : text) { freqMap[c]++; } HuffmanNode* root = buildHuffmanTree(freqMap); unordered_map<char, string> codes; generateHuffmanCodes(root, "", codes); string compressedText = compressText(text, codes); cout << "Compressed Text: " << compressedText << endl; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值