哈夫曼树,哈夫曼编码及应用——(代码实现)

news2024/11/18 18:38:33

哈夫曼树,哈夫曼编码及应用

  • 1.哈夫曼树
    • 1.1 什么是哈夫曼树
  • 2.如何构造哈夫曼树(哈夫曼算法)
    • 2.1 举例实现哈夫曼树
      • 2.1.1手动实现具体步骤
      • 2.1.2代码实现具体步骤
  • 3.哈夫曼编码
    • 3.1 什么是哈夫曼编码
    • 3.2哈夫曼编码的具体实现
  • END!!!

1.哈夫曼树

路径长度:从树中一个节点到另一个节点之间的分支构成两个节点之间的路径,路径上的分支数目就成为路径长度。
树的路径长度:从树根到每一节点的路径长度之和。
带权路径长度:考虑到带权的节点,节点的带权路径长度是从该节点到根之间的路径长度与节点权的乘积。
带权路径长度(WPL):树中所有叶子节点的带权路径长度之和。

在这里插入图片描述
在上图中,根节点到节点D的路径长度是4,二叉树的路径长度=1+1+2+2+3+3+4+4=20。WPL(a)=51+152+403+304+10*4=315。

1.1 什么是哈夫曼树

假设有n各权值{w1,w2,…,wn},构造一棵有n各叶子节点的二叉树,每个叶子节点带权wk,每个叶子的路径长度为lk,WPL=w1l1+w2l2+…+wn*ln,记带权路径长度WPL最小的二叉树为哈夫曼树,也称其为最优二叉树。

2.如何构造哈夫曼树(哈夫曼算法)

  1. 根据给定的n个权值{w1,w2,…,wn}构成n个二叉树的集合F={T1,T2,…,Tn},其中每棵二叉树Ti中只有一个带权为wi的根节点,其左右子树均为空。
  2. 在F中选取两棵根节点的权值最小的树作为左右子树构造一个新的二叉树,且置新的二叉树的根节点的权值为其左右子树上根节点的权值之和。
  3. 在F中删除这两棵树,同时将新得到的二叉树加入F中。
  4. 重复步骤2和步骤3,直到F只含一棵树为止。这棵树便是哈夫曼树。

2.1 举例实现哈夫曼树

2.1.1手动实现具体步骤

在这里插入图片描述
在这里插入图片描述

2.1.2代码实现具体步骤

注意:二叉树的存储以数组形式保存,而且0下标不存储元素,i下标的左孩子2*i,右孩子2*i+2.(之前有关二叉树的文章介绍过,大家可以自行查看)。
#include <iostream>
#include <vector>
#include <string>
#include <queue>

using namespace std;

typedef unsigned int WeigthType;

typedef unsigned int NodeType;

typedef struct
{
    WeigthType weight;
    NodeType parent, leftchild, rightchild;
}HNode;

typedef struct IndexWeight
{
    int index;
    WeigthType weight;
    operator WeigthType() const { return weight; }//按照权值比较
};



class HuffManTree
{
private:
    vector<WeigthType> w;
    vector<HNode> hft;
    int n = 0;
public:
    //构造函数(初始化Huffman树)  (叶子节点树,权重,hufman树)
    HuffManTree(vector<WeigthType> w1)
    {
        w = w1;
        n = w1.size();
        hft.resize(n * 2);
        
        for (int i = 1; i <= n; i++)
        {
            hft.at(i).weight = w[i-1];
        }        
    }

    void CreatHuffman()
    {
        //优先级队列
        priority_queue<IndexWeight, vector<IndexWeight>, std::greater<IndexWeight>> qu;
        for (int i = 1; i <= n; i++)
        {
            qu.push(IndexWeight{ i,hft.at(i).weight });
        }

        int k = n +1;

        while (!qu.empty())
        {
            //左节点
            if (qu.empty()) break;
            IndexWeight left = qu.top();      qu.pop();
            //右节点
            if (qu.empty()) break;
            IndexWeight right = qu.top();     qu.pop();

            hft.at(k).weight = left.weight + right.weight;
            hft.at(k).leftchild = left.index;
            hft.at(k).rightchild = right.index;
            hft.at(left.index).parent = k;
            hft.at(right.index).parent = k;
            qu.push(IndexWeight{ k,hft.at(k).weight });
            k++;            
        }
    }

    void PrintTree()
    {
        int lenght = hft.size();
       
        for (int i = 1; i < lenght; i++)
        {
            cout << "index:" << i
                << "\tweight:" << hft.at(i).weight
                << "\tparent:" << hft.at(i).parent
                << "\tleftchile:" << hft.at(i).leftchild
                << "\trightchild:" << hft.at(i).rightchild << endl;                    
        }
        cout << endl;
    }
    vector<HNode> GetHft()
    {
        return hft;
    }
};
int main()
{
    vector<WeigthType> w{ 5,29,7,8,14,23,3,11 };
    
    HuffManTree tree(w);
    tree.CreatHuffman();
    cout << "打印哈夫曼树:" << endl;
    tree.PrintTree();    

    return 0;
}

在这里插入图片描述

3.哈夫曼编码

哈夫曼树是为了解决远距离通信的数据传输的最优化问题。
比如,有一段文字内容为ABCDEFGH,现需要将其传输给别人,通常就会用二进制来表示每个字符,如下(A的ASCII码为65):

字母ABCDEFGH
二进制字符100 0001100 0010100 0011100 0100100 0101100 0110100 0111100 1000

按照上述所说,传输的内容变为:“100 0001100 0010100 0011100 0100100 010100 0110100 0111100 1000”接受时可以三位来解码。但是当文档很大时,所需要的二进制串是非常可怕的,而且有些字符出现的频率是非常高的。

为提高数据传输的效率,一般采用哈夫曼编码对数据进行压缩,随着字符的增多和多字符权重的不同,这种压缩会更加显出优势。

3.1 什么是哈夫曼编码

 前缀编码:编码中非0即1,长度不等的话其实很容易混淆,若要设计长短不等的编码,则必须是任一字符的编码都不是另一个字符的编码的前缀,这种编码称为前缀编码。
一般地,设需要编码的字符集为{d1,d2,...,dn},
	各个字符在电文中出现的次数或频率集合为{w1,w2,...,wn},以d1,d2,...,dn作为叶子节点,
	以w1,w2,...,wn作为相应叶子节点的权值来构造一棵哈夫曼树。
	规定哈夫曼树的左分支表示0,右分支表示1,则从根节点到叶子节点所经过的路径分支组成的0和1的序列便为该节点对于字符的编码,这就是哈夫曼编码。

3.2哈夫曼编码的具体实现

字母ABCDEFGH
出现的频率*100529781423311
二进制字符111111011100001100111110001

在这里插入图片描述

#include <iostream>
#include <vector>
#include <string>
#include <queue>

using namespace std;

typedef unsigned int WeigthType;

typedef unsigned int NodeType;

typedef struct
{
    WeigthType weight;
    NodeType parent, leftchild, rightchild;
}HNode;

typedef struct IndexWeight
{
    int index;
    WeigthType weight;
    operator WeigthType() const { return weight; }//按照权值比较
};



class HuffManTree
{
private:
    vector<WeigthType> w;
    vector<HNode> hft;
    int n = 0;
public:
    //构造函数(初始化Huffman树)  (叶子节点树,权重,hufman树)
    HuffManTree(vector<WeigthType> w1)
    {
        w = w1;
        n = w1.size();
        hft.resize(n * 2);

        for (int i = 1; i <= n; i++)
        {
            hft.at(i).weight = w[i-1];
        }        
    }

    void CreatHuffman()
    {
        //优先级队列
        priority_queue<IndexWeight, vector<IndexWeight>, std::greater<IndexWeight>> qu;//按照下标比较

        for (int i = 1; i <= n; i++)
        {
            qu.push(IndexWeight{ i,hft.at(i).weight });
        }

        int k = n +1;

        while (!qu.empty())
        {
            //左节点
            if (qu.empty()) break;
            IndexWeight left = qu.top();      qu.pop();
            //右节点
            if (qu.empty()) break;
            IndexWeight right = qu.top();     qu.pop();

            hft.at(k).weight = left.weight + right.weight;
            hft.at(k).leftchild = left.index;
            hft.at(k).rightchild = right.index;
            hft.at(left.index).parent = k;
            hft.at(right.index).parent = k;
            qu.push(IndexWeight{ k,hft.at(k).weight });
            k++;            
        }
    }

    void PrintTree()
    {
        int lenght = hft.size();
       
        for (int i = 1; i < lenght; i++)
        {
            cout << "index:" << i
                << "\tweight:" << hft.at(i).weight
                << "\tparent:" << hft.at(i).parent
                << "\tleftchile:" << hft.at(i).leftchild
                << "\trightchild:" << hft.at(i).rightchild << endl;             
        }
        cout << endl;
    }
    vector<HNode> GetHft()
    {
        return hft;
    }
};

typedef struct HuffmanCodeNode
{
    char ch;
    string code;
}HuffmanCodeNode;

class HuffManCode:public HuffManTree
{
private:
    vector<HuffmanCodeNode> huffcode;
    vector<HNode> hft;    
public:
    HuffManCode(const vector<char> *ch1, vector<WeigthType> w1):HuffManTree(w1)
    {
        int length = ch1->size();
        huffcode.resize(length+1);//0下标不用,从1下标开始
        for (int i = 1; i <= length; i++)
        {
            huffcode.at(i).ch = ch1->at(i-1);            
        }        
    }
    void CreateHuffmanCode()
    {
        hft = HuffManTree::GetHft();
        int length = huffcode.size();
        
        for (int i = 1; i < length; i++)//只需计算0-8下标的huffman编码
        {
            string code1;
            int k = length;
            int c = i;
            int parent = hft.at(c).parent;
            while (parent != 0)
            {
                string temp = hft.at(parent).leftchild == c ? "0" : "1";
                //code1.insert(0, temp);
                huffcode.at(i).code.insert(0, temp);
                c = parent;
                parent = hft.at(c).parent;
            }
        }
    }
    void PrintCode()
    {
        int length = huffcode.size();
        for (int i = 1; i < length; i++)
        {
            cout << "data:" << huffcode.at(i).ch << " --> " << hft.at(i).weight
                << " --> " << "huffmancode:" << huffcode.at(i).code << endl;
        }
    }
};

int main()
{
    vector<WeigthType> w{ 5,29,7,8,14,23,3,11 };
    vector<char> ch{ 'A','B','C','D','E','F','G','H' };
    
    HuffManCode hc(&ch,w);
    
    hc.CreatHuffman();
    hc.CreateHuffmanCode();
    cout << "打印哈夫曼树:" << endl;
    hc.PrintTree();
    cout << "打印哈夫曼编码:" << endl;
    hc.PrintCode();   

    return 0;
}

在这里插入图片描述

END!!!

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

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

相关文章

零基础可以学习Python吗?转行小白怎么学Python?

ython学习常用的技巧有这些&#xff1a;一是要明确自己的学习目的;二是从易到难&#xff0c;循序渐进;三是合理的选择资料&#xff0c;有所取舍;四是坚定自己的信念。以下是关于Python学习技巧的具体介绍。 1、明确自己的学习目的 每个人学Python的初衷绝对是不一样的&#xf…

【观察】Akamai:向分布式云迈出坚实一步,让云和边缘“无处不在”

近年来&#xff0c;云正如同日常生活中的水、电那样&#xff0c;融入到社会的各个层面&#xff0c;它不再是一种单纯的架构或者技术&#xff0c;而是千行百业走向数字化的核心基础设施&#xff1b;云也正在变成一种融合剂&#xff0c;无论是大数据、人工智能、物联网等&#xf…

多目标背包问题:MOJAYA求解多目标背包问题(Multi-objective Knapsack Problem,MOKP)提供Matlab代码

一、多目标背包问题 1.1多目标背包问题描述 多目标背包问题(Multi-objective Knapsack Problem&#xff0c;MOKP)是一种重要的组合优化问题&#xff0c;在生活的许多领域都有着十分广泛的应用。多目标背包问题可以描述为&#xff1a;给定一个背包和n种物品&#xff0c;其中&a…

docker-compose安装gogs

1.gogs是什么 Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发&#xff0c;并且支持 Go 语言支持的 所有平台&#xff0c;包括 Linux、Mac OS X、Windows 以及 ARM 平台。 2.准备工作gogs 在安装软…

论文精读:《FCOS3D: Fully Convolutional One-Stage Monocular 3D Object Detection》

文章目录论文精读摘要&#xff08;Abstract&#xff09;1. 介绍&#xff08;Introduction&#xff09;2. 相关工作&#xff08;Related Work&#xff09;3. 方法&#xff08;Approach&#xff09;3.1 框架总览(Framework Overview)3.2 2D引导的多层次3D预测(2D Guided Multi-Le…

【java】javac 相关API JavaCompiler StandardJavaFileManager AbstractProcessor

1.概述 转载并且补充:Java 编译器 javac 笔记:javac API、注解处理 API 与 Lombok 原理 看这个文章之前首先看:【java】java JSR 269 自定义注解实战 Lombok @Data注解 java版本直接调用 javac 是 Java 代码的编译器 [openjdk, oracle ],初学 Java 的时候就应该接触过。…

马上跨年了,如何用代码写一个“跨年倒计时”呢?

前言 大家好&#xff0c;我是陈橘又青&#xff0c;再过两周就是新的一年了&#xff0c;作为一名有仪式感的程序员&#xff0c;今天我们就来制作一个简单的跨年倒计时小网页&#xff0c;祝看到的所有人新年快乐&#xff01;&#xff08;附上完整源码&#xff0c;需要的小伙伴自取…

八、Docker 安装Mysql(流程、注意点、实例)

Docker 安装mysql 要不 安装tomcat 稍微复杂些,要配置一些参数,例如mysql密码,配置文件编写等。 1、docker hub上面查找mysql镜像 地址:Docker Hub 可以拉取最新的,也可以按照tag搜索自己想要的版本,拉取 2、从docker hub上拉取mysql5.7镜像到本地 命令:docker pull …

【实时数仓】DWM层设计模式、独立访客(UV)的计算

文章目录一 DWS层与DWM层的设计1 设计思路2 DWS层需求分析二 DWM层-UV计算1 需求分析与思路2 从kafka中读取数据&#xff08;1&#xff09;代码实现&#xff08;2&#xff09;测试&#xff08;3&#xff09;总结3 UV过滤 -- 独立访客计算&#xff08;1&#xff09;实现思路&…

Spring+SpringMVC+MP登录案例(含拦截器)

技术框架 后端&#xff1a;Spring、Spring MVC、Mybatis-Plus 前端&#xff1a;HTML、CSS、Layui、JS、Jquery 功能模块技术 1、用户的每一个请求使用了SpringMVC 拦截器技术&#xff0c;没有登录的用户自动重定向到登录页 2、统一请求模式&#xff0c;使用Restful风格对后端…

贤鱼的刷题日常(数据结构栈学习)-1551:Sumsets--题目详解

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;例题讲解1551:Sumsets ✅创作者&#xff1a;贤鱼 ⏰预计时间&#xff1a;25分钟 &#x1f389;个人主页&#xff1a;贤鱼的个人主页 &#x1f525;专栏系列&#xff1a;c &#x1f341;贤鱼的个人社区&#xff0c;欢迎你的…

学Python的理由有哪些?这四大理由足够了

学Python的理由有哪些&#xff1f;可能有人会说Python是一种计算机语言&#xff0c;具有简洁性、易读性、及可扩展性&#xff0c;相对于其他语言学起来会更加容易&#xff0c;目前应用也非常广泛等等。其实总结起来&#xff0c;学Python的理由不外乎四点&#xff0c;即丰富免费…

Python数据分析主要功能是什么?可以用来做什么?

Python是一种计算机程序设计语言&#xff0c;具有简洁性、易读性以及可扩展性&#xff0c;相较于其他语言学习起来更加容易。随着互联网的发展&#xff0c;Python知识也被越来越多的人所熟知。但还是有很多人不了解它究竟可以用来做什么&#xff0c;接下来就跟随我了解一下吧&a…

【轻量级开源ROS 的机器人设备(5)】--(2)拟议的框架——µROS节点

接上文&#xff1a; 【轻量级开源ROS 的机器人设备&#xff08;5&#xff09;】--&#xff08;1&#xff09;拟议的框架——ROS节点 四、开发工具 为了方便用户应用程序的开发&#xff0c;一个代码生成器&#xff0c;一个 堆栈使用分析器和演示项目包含在框架中包裹。 4.1 代…

截止12.17 bitahub踩坑,mask无数次更改,lama代码的那些痛,羊了个羊

前面那篇跑出了STCN&#xff0c;倒是STCN熟悉了很多了 对bitahub&#xff0c;需要注意一个问题 要进ssh请用debug卡&#xff01;&#xff01;&#xff01;&#xff01; 要进ssh请用debug卡&#xff01;&#xff01;&#xff01;&#xff01; 要进ssh请用debug卡&#xff01;&…

数据库文档展示工具

实用工具&#xff1a;数据库文档展示工具 简介 数据库文档展示工具&#xff08;database doc&#xff09;&#xff0c;又叫数据库注释浏览工具&#xff0c;是一个简单的数据库展示各个字段注释的开源工具。在日常开发工作中&#xff0c;您有否这样的体验&#xff1f; 想给前…

干货 | 数字经济创新创业——数字技术创造新经济

下文整理自清华大学大数据能力提升项目能力提升模块课程“Innovation & Entrepreneurship for Digital Economy”&#xff08;数字经济创新创业课程)的精彩内容。主讲嘉宾&#xff1a;Kris Singh: CEO at SRII, Palo Alto, CaliforniaVisiting Professor of Tsinghua Unive…

Elasticsearch 多索引搜索 多条件筛选 去除重复数据

Elasticsearch 多索引搜索 多条件筛选先看结构 分别是索引media_data_es,live_room_essearch_type :dfs_query_then_fetch 不重复复合查询 复合查询就是把一些简单查询组合在一起实现更复杂的查询需求&#xff0c;除此之外&#xff0c;复合查询还可以控制另外一个查询的行为。 …

Spring MVC介绍

Spring MVC 简介什么是Spring MVC了解 MVCMVC 和Spring MVC的联系如何创建一个Spring Web项目在Spring Web 项目中&#xff0c;如何连接Http请求Controller注解可以用其他类注解代替吗连接其他类型的请求如何获取请求中的数据获取单个请求参数获取多个请求参数获取对象获取表单…

高通平台开发系列讲解(DSI篇)DSI层在拨号中的调用逻辑

沉淀、分享、成长,让自己和他人都能有所收获!😄 📢DSI层在拨号中起到的是承上启下的作用。 拨号初始化: 通过mcm_data_init_srv接口调用dsi_init_ex接口,而dsi_init_ex接口进一步通过依次调用dsi_init_cb_func来初始化注册回调、dsi_init_cb_data来初始化数据回调及dsi…