哈夫曼树、哈夫曼编码/解码

news2024/11/16 8:39:52

哈夫曼树

哈夫曼树的基本介绍

哈夫曼树构建步骤图解

创建哈夫曼树代码实现

""" 创建哈夫曼树 """
class EleNode:
    """ 节点类 """
    def __init__(self, value: int):
        self.value = value
        self.left = None  # 指向左子节点
        self.right = None  # 指向右子节点

    def __str__(self):
        return f"Node [value={self.value}]"

    def pre_order(self):
        """前序遍历二叉树"""
        if self is None:
            return
        # 先输出父节点
        print(self, end=' => ')
        # 左子树不为空则递归左子树
        if self.left is not None:
            self.left.pre_order()
        # 右子树不为空则递归右子树
        if self.right is not None:
            self.right.pre_order()


class HuffmanTree:
    arr: list = None

    def __init__(self, arr):
        self.arr = arr

    def create_huffman_tree(self) -> EleNode:
        # 对 arr 列表进行升序排序
        self.arr.sort()
        # 遍历数组,将每个数组元素构建成一个 Node 节点
        # 将 Node 节点加入到一个新列表中
        node_list = []
        for i in self.arr:
            node_list.append(EleNode(i))

        # 循环进行以下步骤,直到列表中只剩下一棵二叉树,此时该二叉树就是哈夫曼树
        while len(node_list) > 1:
            # 取出权值序列中最小的两课二叉树(即列表中的前两个元素)构建一棵新的二叉树
            left = node_list.pop(0)
            right = node_list.pop(0)
            # 新二叉树的根节点的权值=两个节点权值之和
            parent = EleNode(left.value + right.value)
            # 让新二叉树的左右节点分别指向两个最小的节点
            parent.left = left
            parent.right = right
            # 将新二叉树根据根节点的大小插入到序列中的指定位置
            i = 0
            n = len(node_list)
            while i < n:
                if node_list[i].value >= parent.value:
                    node_list.insert(i, parent)  # 找到了根节点存放的位置
                    break
                i += 1
            else:
                # 循环结束表示新的二叉树的权值最大,在node_list列表中没有比它大的
                # 所以添加到列表最后
                node_list.append(parent)

        # 此时列表中只有一棵二叉树,即所求的哈夫曼树
        # 返回哈夫曼树的根结点
        return node_list[0]


huffman = HuffmanTree([13, 7, 8, 3, 29, 6, 1])
node = huffman.create_huffman_tree()
node.pre_order()

哈夫曼编码

基本介绍

哈夫曼编码原理剖析

哈夫曼编码的实例

思路分析

代码实现

""" 哈夫曼编码 """
class CodeNode:
    def __init__(self, data: int, weight: int):
        self.data = data  # 存放字符对应的ASCII码值
        self.weight = weight  # 存放字符在字符串中出现的次数
        self.left = None
        self.right = None

    def __str__(self):
        return f"Node[data={chr(self.data) if self.data else None}, weight={self.weight}]"

    def pre_order(self):
        """二叉树的前序遍历"""
        print(self)
        if self.left:
            self.left.pre_order()
        if self.right:
            self.right.pre_order()


class HuffmanCode:
    # 哈夫曼编码表
    # 存储每个字符对应的编码
    # key为对应的字符,val为字符对应的编码
    huffman_code_tab = {}
    # 记录哈夫曼二进制编码字符串最后一个段的长度
    # 即会将哈夫曼二进制字符串按八位进行分割,分割到最后一个时长度可不为8
    # 所以用一个变量存储最后一段二进制字符串的长度,在解码的时候会用到
    last_char_len = 0

    def create_huffman_tree(self, s: str) -> CodeNode:
        """
        构建哈夫曼编码二叉树
        :param s: 要编码的字符串
        :return:
        """
        # 遍历字符串,统计每一个字符出现的次数,并将结果放入字典
        kw = {}
        for ch in s:
            ascii_code = ord(ch)
            if kw.get(ascii_code):  # 如果该字符出现过,则直接将其次数加1
                kw[ascii_code] += 1
            else:  # 如果没出现过,则出现次数为1
                kw[ascii_code] = 1

        # 按照字符出现的次数对字典进行排序
        kw = sorted(kw.items(), key=lambda kv: (kv[1], kv[0]))
        # 遍历字典,将每个元素构建成一个 Node 节点
        # 将 Node 节点加入到一个新列表中
        node_list = []
        for k, v in kw:
            # print(chr(k),'=', v, end=', ')
            node_list.append(CodeNode(k, v))

        # 循环进行以下步骤,直到列表中只剩下一棵二叉树,此时该二叉树就是哈夫曼树
        while len(node_list) > 1:
            # 取出权值序列中最小的两课二叉树(即列表中的前两个元素)构建一棵新的二叉树
            left = node_list.pop(0)
            right = node_list.pop(0)
            # 新二叉树的根节点的权值=两个节点权值之和
            parent = CodeNode(None, left.weight + right.weight)
            # 让新二叉树的左右节点分别指向两个最小的节点
            parent.left = left
            parent.right = right
            # 将新二叉树根据根节点的大小插入到序列中的指定位置
            n = len(node_list)
            i = 0
            while i < n:
                if node_list[i].weight >= parent.weight:
                    node_list.insert(i, parent)  # 找到了根节点存放的位置
                    break
                i += 1
            else:
                # 循环结束表示新的二叉树的权值最大,在node_list列表中没有比它大的
                # 所以添加到列表最后
                node_list.append(parent)

        # 此时列表中只有一棵二叉树,即所求的哈夫曼树
        # 返回哈夫曼树的根结点
        return node_list[0]

    def get_huffman_code_tab(self, ele_node: CodeNode, code: str, code_str: str):
        """
        遍历所创建的哈夫曼树,得到所有叶子节点(叶子结点即要得到的字符)的编码
        这里规定左节点为0,右节点为1
        :param ele_node: 传入的要遍历的树的根节点,初始为根节点
        :param code: 表示所选择的路径是左节点还是右节点
        :param code_str: 每个字符对应的编码
        :return:
        """
        code_str += code  # 拼接编码
        if ele_node.data is None:
            # 表示是非叶子节点,因为在创建哈夫曼树时设置了为叶子结点的data为空
            # code_str += code
            if ele_node.left:
                self.get_huffman_code_tab(ele_node.left, '0', code_str)
            if ele_node.right:
                self.get_huffman_code_tab(ele_node.right, '1', code_str)
        else:  # 是叶子节点
            self.huffman_code_tab[chr(ele_node.data)] = code_str

    def huffman_zip(self, s: str) -> list:
        """
        利用哈夫曼编码表把字符串中的每一个字符转换成对应的编码
        即将一个字符串根据哈夫曼编码进行压缩,得到一个压缩后的结果
        :param s: 要转换的字符串
        :return: 返回编码后的列表
        """
        res = ''
        # 遍历字符串,将每一个字符转换成对应的编码,并将所有编码拼接起来
        # "i like like like java do you like a java" => 以下形式
        # 1100111111101110001011111110111000101111111011100010100001011000101011
        # 001100001011001000011001110111111101110001011010100001011000101
        for i in s:
            res += self.huffman_code_tab[i]
        # 将得到的编码字符串按八位进行分割,将每八位转换成一个int,并将int存放到列表中
        code_list = []
        i = 0
        n = len(res)
        while i < n:
            num = int(res[i:i + 8], 2)  # 将二进制字符串转换为整数
            code_list.append(num)
            i += 8
            if i < n <= i + 8:  # 已经分割到了最后一部分,记录该部分的长度
                self.last_char_len = n - i

        return code_list

    def huffman_decode(self, code_list) -> str:
        """
        将哈夫曼编码进行解压,得到一个可阅读的字符串
        :param code_list: 要解压的哈夫曼编码列表
        :return: 解码后的字符串
        """
        # 将哈夫曼编码列表转换成对应的二进制字符串
        # [415, 476, 95, 476, 95, 476, 80, 177, 345, 389, 400, 206, 254, 226, 212, 44, 5] =>
        # 1100111111101110001011111110111000101111111011100010100001011000101011
        # 001100001011001000011001110111111101110001011010100001011000101
        code_str = ''  # 存储对应的二进制字符串
        count = 0
        n = len(code_list)
        for i in code_list:
            t = "{:08b}".format(i)  # 将整数转换为二进制字符串
            code_str += t
            count += 1
            if count == n - 1:
                break
        code_str += "{:0{k}b}".format(code_list[count], k=self.last_char_len)

        # 将哈夫曼编码表的键值互换
        # 比如原来的是'a': '001' => 变成 '001': 'a'
        code_tab = {}
        for k, v in self.huffman_code_tab.items():
            code_tab[v] = k

        # 遍历二进制字符串
        j = 0
        i = 1
        n = len(code_str)
        res_code = ''  # 解码后的字符串
        while i <= n:
            t = code_str[j:i]
            ch = code_tab.get(t)
            if ch:
                res_code += ch
                j = i
            i += 1
        return res_code


s = "i like like like java do you like a java"
# s = "I love python hahha nihao"
huffman = HuffmanCode()
root_node = huffman.create_huffman_tree(s)
huffman.get_huffman_code_tab(root_node, '', '')
huffman_code_list = huffman.huffman_zip(s)
decode_str = huffman.huffman_decode(huffman_code_list)
print(decode_str)

使用哈夫曼编码压缩文件的注意事项(代码胜省略)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121710.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新加坡攻略

文章目录 基础信息入境行李App电信交通餐饮购物法规旅游牛车水&#xff08;Chinatown&#xff09;克拉码头&#xff08;Clarke Quay&#xff09;东海岸&#xff08;East Coast&#xff09;丹戎巴葛&#xff08;Tanjong Pagar&#xff09;滨海湾&#xff08;Marina Bay&#xff…

Android学习之路(21) 进程间通信-AIDL与Servce基本使用

Service 与 Thread 和 进程 之间的关系 进程&#xff1a;应用程序在内存中分配的空间。&#xff08;正在运行中的程序&#xff09;线程&#xff1a;负责程序执行的单元&#xff0c;也称为执行路径。&#xff08;需要线程来执行代码&#xff09;。一个进程至少包含一条线程&…

ip报头和ip报文切片组装问题

在tcp层将数据打包封装向下传递后&#xff0c;网络层将其整个看为一个数据&#xff0c;然后对其数据加网络报头操作&#xff0c;在网络层最具有代表的协议就是ip协议。在这里我们探究ipv4的报头。 ip报头 4位版本&#xff1a;指定ip的版本号&#xff0c;对于ipv4来说就是4。 …

粤嵌实训医疗项目day02(Vue + SpringBoot)

目录 一、创建vue项目并运行 二、vue-cli中的路由使用 三、element-ui框架、实现页面布局以及vue-路由 四、前端登录页面 五、user登录后端接口完善【后端】 六、user登录前端-请求工具-请求发起【前端】 七、请求的跨域-访问策略 八、完善项目的页面布局、导航菜单以及…

反射的作用(可以使用反射保存所有对象的具体信息)

1、绕过 编译阶段 为集合添加数据 反射是作用在运行时的技术&#xff0c;此时集合的泛型将不能产生约束了&#xff0c;此时是可以 为集合存入其他任意类型的元素的 。泛型只是在编译阶段可以约束集合只能操作某种数据类型&#xff0c;在 编译成Class文件进入 运行阶段 的时候&a…

存储优化知识复习一详细版解析

存储优化 知识复习一 一、 选择题 1、1948 年&#xff0c;____提出了“信息熵”(shāng) 的概念&#xff0c;解决了对信息的量化度量问题。 A、薛定谔 B、香农 C、克劳修斯 D、纳什 【参考答案】B2、 RAID2.0技术下&#xff0c;LUN是建立在____上。 A、硬盘 B、条带 C、Chun…

RESTful 分享

RESTful 分享 什么是RESTful 理解RESTful RESTful的使用 1.什么是RESTful REST全称是Representational State Transfer&#xff0c;中文译文就是“表述性状态转移”。 在2000年&#xff0c;由Roy Fielding&#xff08;HTTP规范的主要编写者之一&#xff09;在博士论文中提…

鲁迅为什么打周树人?今天的昨天是明天的什么?chatgpt4.0告诉你

GPT-4架构&#xff0c;是OpenAI开发的一个大型语言模型。虽然和GPT-3.5都是基于GPT-3的进一步发展&#xff0c;但是GPT-4在模型大小&#xff0c;知识更新&#xff0c;知识更新等方面都比GPT-3更上一个层次。现在国内很多平台号称可以使用GPT4&#xff0c;而实际上是能申请到GPT…

2023年09月 C/C++(八级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;最短路径问题 平面上有n个点&#xff08;n<100&#xff09;&#xff0c;每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。 若有连线&#xff0c;则表示可从一个点到达另一个点&#xff…

看我为了水作业速通C++!

和java不太一样的一样的标题打个*&#xff0c;方便对比 基本架构* #include<iostream> using namespace std; int main() { system("pause"); return 0; } 打印* cout << "需要打印的内容" <<endl endl 是一个特殊的输出流控…

还在为 Dubbo 服务写 Controller?因为未使用 ShenYu 网关

Dubbo 是一款高性能、轻量级的开源 Java RPC 框架&#xff0c;它可以帮助开发人员快速构建分布式服务。在 Dubbo 应用中&#xff0c;我们经常需要提供 HTTP 调用&#xff0c;如供 H5、外部系统等调用。一般的做法是为需要提供 HTTP 调用的服务编写 Controller&#xff0c;但这并…

高校教务系统登录页面JS分析——西安交通大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密码加…

CUDA学习笔记(八)Branch Divergence and Unrolling Loop

Avoiding Branch Divergence 有时&#xff0c;控制流依赖于thread索引。同一个warp中&#xff0c;一个条件分支可能导致很差的性能。通过重新组织数据获取模式可以减少或避免warp divergence&#xff08;该问题的解释请查看warp解析篇&#xff09;。 The Parallel Reduction …

Mybatis-Plus 0基础光速入门代码

目录 1.创建springboot项目 2.引入依赖 3.找到application.properties&#xff0c;把后缀改成yml&#xff08;这种格式的文件有更多优点&#xff09;&#xff0c;在application.yml里面加上下面的配置代码 4.写实体类 5.创建接口继承BaseMapper 6.在启动类中加上注解Mappe…

头脑风暴之约瑟夫环问题

一 问题的引入 约瑟夫问题的源头完全可以命名为“自杀游戏”。本着和谐友爱和追求本质的目的&#xff0c;可以把问题描述如下&#xff1a; 现有n个人围成一桌坐下&#xff0c;编号从1到n&#xff0c;从编号为1的人开始报数。报数也从1开始&#xff0c;报到m人离席&#xff0c…

阶段七-Day01-Spring01

一、Spring框架介绍 1. 介绍 Spring Framework是由Spring团队研发的模块化、轻量级开源框架。其主要目的是为了简化项目开发。 Spring Framework前身是interface21&#xff0c;由Rod Johnson于2002年研发&#xff0c;主要是为了不使用EJB下依然能够构建高质量Java EE项目。E…

Ubuntu下 u2net tensorrt模型部署

TensorRT系列之 Windows10下yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov7 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov6 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov5 tensorrt模型加速…

浅析人脸活体检测技术的功能及几种分类

在日常生活工作中&#xff0c;出现了人脸验证、人脸支付、人脸乘梯、人脸门禁等等常见的应用场景。这说明人脸识别技术已经在门禁安防、金融行业、教育医疗等领域被广泛地应用&#xff0c;人脸识别技术的高速发展与应用同时也出现不少质疑。其中之一就是人脸识别很容易被照片、…

DDOS攻击的有效防护方式有哪些?

DDoS攻击简介&#xff1a; DDoS攻击&#xff0c;即分布式拒绝服务攻击&#xff08;Distributed Denial of Service&#xff09;&#xff0c;是一种网络攻击&#xff0c;旨在通过向目标服务器发送大量恶意请求&#xff0c;使服务器资源耗尽&#xff0c;无法满足合法用户的需求&a…

KubeSphere安装KubeEdge

1. kubesphere安装请参考博客 2. 配置master节点 控制台->平台管理->集群管理->自定义CRD&#xff0c;搜索​​clusterconfiguration​​&#xff0c;查看详情&#xff0c;在资源列表中&#xff0c;点击 ​​ks-installer​​ 右侧的图标&#xff0c;然后选择编辑配…