树结构的python实现(五:哈夫曼树)

以下为哈夫曼树的python实现:

from collections import Counter
from binary_tree import Node, BinaryTree  # binary_tree的代码见文章:https://blog.csdn.net/moyao_miao/article/details/136787981


class HuffmanNode(Node):
    """哈夫曼树节点类"""
    def __init__(self, key_value):
        """重写构造函数,输入由键值对构成"""
        super().__init__(key_value[1])
        self.key = key_value[0]  # 编码的键


class HuffmanTree(BinaryTree):
    """哈夫曼树类"""
    def __init__(self, data_dict):
        """
        构造函数,使用给定的数据字典初始化哈夫曼树。
        :param data_dict:包含数据项及其对应权重的字典。
        """
        self.node_list = [HuffmanNode(item) for item in data_dict.items()]  # 创建节点列表
        self.root = self._list_to_binarytree()  # 构建哈夫曼树
        self.code_table = {}  # 初始化编码表

    def _list_to_binarytree(self):
        """将节点列表转换为哈夫曼树:使用贪心算法,通过不断选择两个最小权重的节点合并,构建新的哈夫曼树。"""
        while len(self.node_list) > 1:
            self.node_list.sort(key=lambda x: x.value)  # 根据节点权重排序
            left_node = self.node_list.pop(0)  # 弹出最小权重的节点作为左子节点
            right_node = self.node_list.pop(0)  # 弹出次小权重的节点作为右子节点
            node = HuffmanNode((None, left_node.value + right_node.value))  # 创建新节点,其权重为两子节点之和
            node.add(True, left_node)  # 将弹出的节点作为新节点的子节点
            node.add(False, right_node)
            self.node_list.insert(0, node)  # 将新节点添加回列表
        return self.node_list[0]  # 当列表中只剩一个元素时,即为树的根节点

    def _preorder(self, node, code=''):
        """
        重写前序遍历的内部递归函数,使其遍历并记录各叶子节点的编码到编码表。
        :param node: 当前节点
        :param code: 编码
        :return: None
        """
        if node.left_node and node.right_node:
            self._preorder(node.left_node, code + '0')
            self._preorder(node.right_node, code + '1')
        else:self.code_table[node.key] = code

    def get_code_table(self):
        """前序遍历哈夫曼树获取其编码表"""
        self.preorder_traversal()
        return self.code_table


class HuffmanCode():
    def __init__(self, data_list):
        """
        初始化哈夫曼编码器。
        :param data_list:需要被编码的原始数据列表。
        属性:
        code_table (dict): 由哈夫曼树生成的编码表,键为原始数据,值为对应的哈夫曼编码。
        encode_data (str): 原始数据列表经过哈夫曼编码后生成的二进制字符串。
        """
        self.code_table = HuffmanTree(Counter(data_list)).get_code_table()  # 使用Counter统计data_list中元素的频率,然后基于这些频率生成哈夫曼树和对应的编码表
        self.encode_data = ''.join([self.code_table[data] for data in data_list])  # 根据编码表,将data_list中每个元素转换为对应的哈夫曼编码,并拼接成一个字符串

    def encode(self):
        """
        对已生成的二进制编码串进行填充,使其长度成为8的倍数,以便于存储和传输。
        :return: 经过填充处理的二进制编码字符串,最后8位用于记录填充的0的个数。
        """
        zero_num = 8 - len(self.encode_data) % 8  # 计算为了使二进制字符串长度达到8的倍数,需要补充的0的个数
        self.encode_data += '0' * zero_num + '{:08b}'.format(zero_num)  # 在二进制字符串末尾添加相应数量的0,以及一个8位二进制数表示添加的0的个数
        return self.encode_data
        # return ''.join([hex(int(self.encode_data[8*i:8*(i+1)],2)) for i in range(len(self.encode_data)//8)])#转十六进制的方法

    def decode(self):
        """
        解码方法,将存储在实例中的二进制编码字符串还原回原始数据列表。
        处理流程包括:
        1. 从二进制编码字符串尾部读取用于填充的位数信息并移除这些和对应的填充位。
        2. 使用编码表的逆映射(解码表)将二进制编码字符串还原成原始数据。

        ### 改进建议
        1. **效率优化**:当前的解码过程是通过逐字符查找解码表来实现,这在编码数据较长时可能效率不高。考虑优化数据结构以加快查找速度,例如使用更高效的查找算法或数据结构。
        :return:从二进制编码字符串解码得到的原始数据,元素之间无分隔符连续连接。
        """
        zero_num = int(self.encode_data[-8:], 2)  # 从编码字符串末尾的8位读取用于填充的位数
        self.encode_data = self.encode_data[:-8 - zero_num]  # 移除填充的和记录填充位数的数据,获得实际编码内容
        decode_table = {value: key for key, value in self.code_table.items()}  # 构造解码表,即编码表的键值对调换
        index, code, decode_data = 0, '', ''  # 初始化指针、临时代码存储器和解码结果容器
        while index < len(self.encode_data):
            code += self.encode_data[index]
            # 如果临时存储的代码在解码表中,解码对应的字符,并重置临时存储器
            if code in decode_table:
                decode_data += decode_table[code]
                code = ''
            index += 1
        return decode_data


if __name__ == "__main__":
    obj = HuffmanTree({'a': 13, 'b': 10, 'c': 23, 'd': 6, 'e': 76, 'f': 18, 'g': 3, 'h': 52, 'i': 80})
    obj.plot()
    obj = HuffmanCode('can you can a can as a can canner can a can?')
    print(obj.encode())
    print(obj.decode())

输出:

              281
            125      156
          52     73     76     80
        N    N    31    42    N    N    N    N
      13   18   19   23
    N  N  N  N  9  10  N  N
  3 6 N N


1110100101101101101111101001011101001001101110100100111010110011011101001011101000011001011001110111010010011011101001100000000000000110
can you can a can as a can canner can a can?

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生成哈是一种常用的数据结构和算法,用于实现数据的压缩和编码。下面是生成哈的基本步骤: 1. 统计字符频率:遍历待编码的文本或数据,统计每个字符出现的频率。 2. 构建叶子节点:将每个字符及其频率作为一个叶子节点。 3. 构建哈:从频率最低的两个节点开始,合并它们并创建一个新的父节点,该父节点的频率为两个子节点的频率之和。将新的父节点插入到节点集合中,并删除原来的两个子节点。重复这个过程,直到只剩下一个根节点。 4. 生成编码表:从根节点开始,遍历哈的路径,左子为0,右子为1,将每个字符对应的编码保存在编码表中。 5. 进行编码:根据生成的编码表,将原始文本或数据中的每个字符替换为对应的编码。 6. 进行解码:使用生成的哈和编码表,将编码后的数据逐个解码为原始字符。 下面是一个简单的Python代码示例来生成哈: ```python import heapq from collections import defaultdict class Node: def __init__(self, char, freq): self.char = char self.freq = freq self.left = None self.right = None def build_huffman_tree(text): # 统计字符频率 freq_dict = defaultdict(int) for char in text: freq_dict[char] += 1 # 构建叶子节点 nodes = [] for char, freq in freq_dict.items(): nodes.append(Node(char, freq)) # 构建哈 heapq.heapify(nodes) while len(nodes) > 1: left_node = heapq.heappop(nodes) right_node = heapq.heappop(nodes) parent_node = Node(None, left_node.freq + right_node.freq) parent_node.left = left_node parent_node.right = right_node heapq.heappush(nodes, parent_node) return nodes # 示例用法 text = "hello world" huffman_tree = build_huffman_tree(text) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值