【哈夫曼树的构造和查找最小的的权值结点代码,哈夫曼编码的算法实现】

news2025/3/10 11:21:36

文章目录

  • 哈夫曼树的构造和查找最小的的权值结点代码
  • 哈夫曼编码思想
  • 哈夫曼编码的算法实现

哈夫曼树的构造和查找最小的的权值结点代码

#include<iostream>
using namespace std;

typedef struct {
	int parent, lch, rch;//双亲结点和孩子结点的下标
	int weight;//权值
}htNode,*HuffmanTree;

void SelectHuff(HuffmanTree& HT, int n, int& s1, int& s2);

//构造哈夫曼树----哈夫曼算法
void CreateHuffmanTree(HuffmanTree &HT,int n) {
	int m,i,s1,s2;
	if (n <= 1) {
		return;
	}
	m = 2 * n - 1;//数组共2n-1个元素
	HT = new htNode[m + 1];//0号单元未用,HT[m]表示根结点
	for (i = 1; i <= m; ++i) {
		//将2n-1个元素的lch,rch,parent置为0
		HT[i].lch = 0;
		HT[i].rch = 0;
		HT[i].parent = 0;
	}
	cout << "初始化成功" << endl;
	for (i = 1; i <= n; ++i) {
		cout << "请输入你想要输入的第" << i << "个元素的权值:" << endl;
		cin >> HT[i].weight;//输入前n个元素的weight值
		//初始化结束,下面开始建立哈夫曼表
	}
	for (i = n + 1; i <= m; i++) {//合并产生n-1个结点,构造Huffman树
		SelectHuff(HT, i - 1, s1, s2);
		HT[s1].parent = i;//表示从F中删除s1,s2
		HT[s2].parent = i;

		//s1,s2分别作为i的左右孩子
		HT[i].lch = s1;
		HT[i].rch = s2;
		//i的权值为左右孩子权值之和
		HT[i].weight = HT[s1].weight + HT[s2].weight;
		cout << HT[i].weight <<" " << HT[s1].weight<<" " << HT[s2].weight;
		cout << endl;
	}
	cout << endl;
}

//int Select(HuffmanTree HT, int n, int s1, int s2) {
//	//在HT[K](1<=k<=i-1)中选择两个其双亲域为0,
//	//且权值最小的结点,并返回s1,s2;
//	int k;
//	for (k = 0; k < 2 * n - 1; k++) {
//		if (HT[k].lch = HT[k].rch = 0 && HT[k].weight) {
//			
//			return s1, s2;
//		}
//	}
//}

//查找最小的权值的两个结点
//1.构造森林全是根;2.选用两小造新树;
//3.删除两小添新人;4.重复2, 3剩单根;
void SelectHuff(HuffmanTree &HT, int n, int &s1, int &s2) {
	int i;
	int minum;//定义一个临时变量保存最小值
	for (i = 1; i <= n; i++) {
		//以下是找到第一个最小值
		if (HT[i].parent == 0) {
			minum = i;
			break;
		}
	}
	for (i = 1; i <= n; i++) {
		if (HT[i].parent == 0) {
			if (HT[i].weight < HT[minum].weight)
				minum = i;
		}
	}
	s1 = minum;
	//以下是找第二个最小值,且与第一个不同
	for (i = 1; i <= n; i++) {
		if (HT[i].parent == 0 && i != s1) {
			minum = i;
			break;
		}
	}
	for (i = 1; i <= n; i++) {
		if (HT[i].parent == 0 && i != s1) {
			if (HT[i].weight < HT[minum].weight) {
				minum = i;
			}
		}
	}
	s2 = minum;
}

int main() {
	HuffmanTree ht = nullptr;
	int n;
	cout << "请输入你想输入多少个值:" << endl;
	cin >> n;
	CreateHuffmanTree(ht, n);
	return 0;
}

哈夫曼编码思想

关键:要设计长度不等的编码,则必须使任一字符的编码都不是另一字符编码的前缀。
方法:1.统计字符集中每个字符在电文中出现的平均概率(概率越大,要求编码越短)。
2.利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短。
3.在哈夫曼树的每个分支上标为0或1:
结点的左分支标0,右分支标1;
把从根到每个叶子的路径上的标号连接起来,作为叶子代表的字符的编码。
在这里插入图片描述
问题:为什么哈夫曼树是前缀编码?
因为没有一片树叶是另外一片树叶的祖先,所以每个叶结点的编码就不可能是其他叶结点的前缀。
为什么哈夫曼编码能够保证字符编码总长最短?
因为哈夫曼树的带权路径长度最短,故字符编码的总长最短。

哈夫曼编码的算法实现

在这里插入图片描述

从哈夫曼树到哈夫曼数组
以下是算法的推导过程:
在这里插入图片描述
例如是找D的哈弗曼编码:
先找到D在哈夫曼树中的位置,然后查看D的双亲结点,在哈夫曼数组表中找到双亲0.09的位置在i= 9,所以在i=9这一排查找D在他这里是左子树还是右子树,这里是右子树所以最后一位编码就是1.然后再向前以此类推。
代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include<string.h>
#define nodeenum 10//叶子结点数

typedef struct {
	int parent, lch, rch;//双亲结点和孩子结点的下标
	int weight;//权值
}htNode,*HuffmanTree;

//哈夫曼编码
typedef char** huffmanCode;//第一个*代表的是指针变量,说明他是数组。
//第二个*说明他是指针数组,代表这个char类型数组里面的每一个元素都是*huffmanCode变量

void SelectHuff(HuffmanTree& HT, int n, int& s1, int& s2);

//构造哈夫曼树----哈夫曼算法
void CreateHuffmanTree(HuffmanTree &HT,int n) {
	int m,i,s1,s2;
	if (n <= 1) {
		return;
	}
	m = 2 * n - 1;//数组共2n-1个元素
	HT = new htNode[m + 1];//0号单元未用,HT[m]表示根结点
	for (i = 1; i <= m; ++i) {
		//将2n-1个元素的lch,rch,parent置为0
		HT[i].lch = 0;
		HT[i].rch = 0;
		HT[i].parent = 0;
	}
	cout << "初始化成功" << endl;
	for (i = 1; i <= n; ++i) {
		cout << "请输入你想要输入的第" << i << "个元素的权值:" << endl;
		cin >> HT[i].weight;//输入前n个元素的weight值
		//初始化结束,下面开始建立哈夫曼表
	}
	for (i = n + 1; i <= m; i++) {//合并产生n-1个结点,构造Huffman树
		SelectHuff(HT, i - 1, s1, s2);
		HT[s1].parent = i;//表示从F中删除s1,s2
		HT[s2].parent = i;

		//s1,s2分别作为i的左右孩子
		HT[i].lch = s1;
		HT[i].rch = s2;
		//i的权值为左右孩子权值之和
		HT[i].weight = HT[s1].weight + HT[s2].weight;
		cout << HT[i].weight <<" " << HT[s1].weight<<" " << HT[s2].weight;
		cout << endl;
	}
	cout << endl;
}

//int Select(HuffmanTree HT, int n, int s1, int s2) {
//	//在HT[K](1<=k<=i-1)中选择两个其双亲域为0,
//	//且权值最小的结点,并返回s1,s2;
//	int k;
//	for (k = 0; k < 2 * n - 1; k++) {
//		if (HT[k].lch = HT[k].rch = 0 && HT[k].weight) {
//			
//			return s1, s2;
//		}
//	}
//}

//初始化
int initHuffmanTree(HuffmanTree& HT) {
	int i;
	HT = new htNode[2 * nodeenum];
	for (i = 1; i <= 2 * nodeenum - 1; i++) {
		HT[i].parent = HT[i].lch = HT[i].rch = -1;
	}
	cout << "请输入权值:" << endl;
	for (i = 1; i <= nodeenum; i++) {
		cin >> HT[i].weight;
	}
	return 1;
}

//查找最小的权值的两个结点
//1.构造森林全是根;2.选用两小造新树;
//3.删除两小添新人;4.重复2, 3剩单根;
void SelectHuff(HuffmanTree &HT, int n, int &s1, int &s2) {
	int i;
	int minum;//定义一个临时变量保存最小值
	for (i = 1; i <= n; i++) {
		//以下是找到第一个最小值
		if (HT[i].parent == 0) {
			minum = i;
			break;
		}
	}
	for (i = 1; i <= n; i++) {
		if (HT[i].parent == 0) {
			if (HT[i].weight < HT[minum].weight)
				minum = i;
		}
	}
	s1 = minum;
	//以下是找第二个最小值,且与第一个不同
	for (i = 1; i <= n; i++) {
		if (HT[i].parent == 0 && i != s1) {
			minum = i;
			break;
		}
	}
	for (i = 1; i <= n; i++) {
		if (HT[i].parent == 0 && i != s1) {
			if (HT[i].weight < HT[minum].weight) {
				minum = i;
			}
		}
	}
	s2 = minum;
}

//构造哈夫曼编码
void CreateHuffmanCode(HuffmanTree HT,huffmanCode HC,int n){
	int i;
	int start = 0, c = 0, f = 0;//start记录cd数组的下标,c初始为叶子结点下标,就是孩子结点的下标,
	//f记录的是双亲结点的下标
	//从叶子到根逆向求每一个字符的哈夫曼编码,存储在编码表HC中
		HC = new char*[n + 1];//分配存储n个字符编码的编码表空间
		char *cd = new char[n];//分配临时存放每个字符编码的动态数组空间
		cd[n - 1] = '\n';//编码结束符
		for (i = 1; i <= n; i++) {
			start = n - 1;//start开始时指向最后,即编码结束的位置
			c = i;
			f = HT[c].parent;//f指向结点c的双亲结点
			while (f!=-1)//从叶子结点开始向上回溯,直到到达根结点
			{
				start--;//回溯依次start先前指向一个一个位置
				if (HT[f].lch == c) {
					cd[start] = '0';//结点c是左孩子就生成代码0
				}
				else {
					cd[start] = '1';//若结点c是右孩子就生成代码1
				}
				c = f; f = HT[c].parent;//继续向上回溯,求出第i个字符的编码
			}
			HC[i] = new char[n - start];//为第i个字符分配存储空间
			strcpy(HC[i], &cd[start]);//将求得的代码从临时空间cd复制到HC的当前行中
		}
		delete cd;//释放临时空间
}

int main() {
	HuffmanTree ht = nullptr;
	huffmanCode hc;
	int n;
	cout << "请输入你想输入多少个值:" << endl;
	cin >> n;
	CreateHuffmanTree(ht, n);
	CreateHuffmanCode(ht, hc, n);
	return 0;
}

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

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

相关文章

SpringBoot3基础特性

SpringBoot3基础特性 SpringApplication 自定义banner 类路径添加banner.txt或设置spring.banner.location就可以定制banner推荐网站:Spring Boot banner在线生成工具&#xff0c;制作下载英文banner.txt,修改替换banner.txt文字实现自定义。 提示&#xff1a; 可以通过修改配…

ChineseChess.2023.11.13.01

中国象棋残局模拟器ChineseChess.2023.11.13.01

react路由安装配置react-router-dom/‘Switch‘ is not defined报错解决

1.安装 npm install --save react-router-dom安装完成 新建两个页面并导出 app.js import Nav from ./components/Nav import Home from ./components/Home import { Link, Route, Switch } from react-router-domfunction App() {return (<div><div><p>&…

二十四、W5100S/W5500+RP2040树莓派Pico<PHY的状态模式控制>

文章目录 1. 前言2. 相关简介2.1 简述2.2 原理2.3 优点&应用 3. WIZnet以太网芯片4. PHY模式配置测试4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 测试现象 5. 注意事项6. 相关链接 1. 前言 W5100S/W5500不仅支持自动PHY自动协商&#xff0c;而且支持用户自定义…

低代码平台如何提高开发效率?

目录 一、开发工具&#xff1a;JNPF 二、产品分析 1可视化应用开发 2流程管理 3特别支持整个平台源码合作 三、使用技巧 四、总结 在当今快速发展的软件开发领域&#xff0c;提高生产效率和质量是每个开发团队追求的目标。JNPF&#xff08;Java Non-Enterprise Applicat…

C/C++:在#define中使用参数

文章目录 在#define中使用参数参考资料 在#define中使用参数 在#define中使用参数可以创建外形和作用与函数类似的类函数宏。带有 参数的宏看上去很像函数&#xff0c;因为这样的宏也使用圆括号。类函数宏定义的圆 括号中可以有一个或多个参数&#xff0c;随后这些参数出现在替…

MASK、MPSK、MFSK信号的调制与解调+星座图

MASK、MPSK、MFSK信号的调制与解调星座图 本文主要涉及多进制幅度键控&#xff08;MASK&#xff09;、多进制相移键控&#xff08;MPSK&#xff09;、多进频移键控&#xff08;MFSK&#xff09;的调制与解调&#xff0c;同时涉及到星座图的分析。 关于通信原理还有其他文章可参…

2023/11/13JAVA学习

字节数组增大的同时,运行速度也会加快,但是大到一定程度就不行了 要想追加数据,要在低级流后面加true,高级流后面加不了 不是乱码,不是让人看的 保持数据一一对应 否则会报错 下载后,拷贝到一个包里,再 comment是你想添加的注释 txt文本也可

宝塔开心版hostcli的广告去除

首先感谢hostcli把宝塔7.6剥离了&#xff0c;直接安装我这里是缺少pyenv的包。 直接进入正题吧。 定位到页面左下方的广告位于 /www/server/panel/BTPanel/templates/default/layout.html “退出”按钮下方有条线开始去掉 去掉之前的忘了截图了&#xff0c;就这样吧&#xff…

幼师一旦开窍,工作真的没有这么难

真心希望所有新手幼教老师都能知道啊 只有输入关键词和要求&#xff0c;几秒就能生成一篇教案&#xff0c;从教学目标到教学内容都能给你安排的妥妥的。而且可以多次生成&#xff0c;每次生成都是不一样的内容。 什么教案、发言稿、总结、评语都能用的上啊&#xff0c;幼师姐…

【Kettle实战】数据分批处理及参数化传递子作业任务

对于大表操作&#xff0c;本来离线数据需要分批处理&#xff0c;刚开始只会用具体日期去做&#xff0c;通过复制多分转换和作业来处理。当日期范围大了后&#xff0c;这是个苦力活儿&#xff0c;kettle里面有参数化传递功能&#xff0c;多动手实操&#xff0c;懂得灵活变通自然…

【PG】PostgreSQL 目录结构

目录 1 软件安装目录 2 数据文件目录 base/&#xff1a;存储每个数据库的基本数据文件 global/&#xff1a;包含了全局性质的系统表空间文件 pg_tblspc/&#xff1a;包含了表空间的符号链接 pg_twophase/&#xff1a;包含了两阶段提交中使用的文件 pg_stat_tmp/&#xff…

Mysql Explain工具介绍

使用EXPLAIN关键字可以模拟优化器执行SQL语句&#xff0c;分析查询语句或是结构的性能瓶颈。 准备表 -- 课程表 CREATE TABLE class (id int(11) NOT NULL,name varchar(45) DEFAULT NULL,update_time datetime DEFAULT NULL,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSET…

msvcr71.dll丢失多种解决方法解析,全方位解读msvcr71.dll文件

在日常使用电脑时&#xff0c;你是否曾遇到过“msvcr71.dll文件丢失”的错误提示&#xff1f;别着急&#xff0c;本文将为你详细介绍msvcr71.dll丢失的解决方法&#xff0c;让你迅速解决这一烦恼。 一.多种msvcr71.dll丢失解决方法 修复方法一:重新安装相应软件 首先&#xf…

QML14、Qt之Q_PROPERTY宏理解

在初学Qt的过程中,时不时地要通过F2快捷键来查看QT类的定义,发现类定义中有许多Q_PROPERTY的东西,比如最常用的QWidget的类定义: Qt中的Q_PROPERTY宏在Qt中是很常用的,那么它有什么作用呢? Qt提供了一个绝妙的属性系统,Q_PROPERTY()是一个宏,用来在一个类中声明一个属…

家电制造产线物料追踪RFID智能管理解决方案

家电行业需求 家电行业的生产节奏快&#xff0c;供应商众多&#xff0c;导致入厂车辆经常出现拥堵和等待的情况&#xff0c;生产线可能因为关键零部件物流未到位而停产&#xff0c;传统的家电制造行业生产物流模式主要依赖人工进行零部件的存储、拣选、配送、核对和发放等环节…

[WiFi] WiFi TPC原理及认证要求

TPC说明 发射功率控制 &#xff08;TPC&#xff09; 是 WLAN 设备使用的一种机制&#xff0c;用于确保缓解因子至少 来自大量设备的总功率为 3 dB。这要求 RLAN 器件的 TPC 范围为对于具有 TPC 的器件&#xff0c;其最低值至少比表 2 中给出的平均 值低 6 dB。 TPC机制及认证要…

C#中.NET Framework 4.8控制台应用通过EF访问已建数据库

目录 一、创建.NET Framework 4.8控制台应用 二、建立数据库 1. 在SSMS中建立数据库Blogging 2.在VS上新建数据库连接 三、安装EF程序包 四、自动生成EF模型和上下文 1.Blog.cs类的模型 2.Post.cs类的模型 3.BloggingContext.cs数据库上下文 五、编写应用程序吧 我们…

四点定球-克拉默法则

一、原理 使用克拉默法则进行四点定球 - 知乎 二、代码实现 c /// <summary> /// 四个不共面的点 用克拉默法则 计算球心和半径 /// </summary> /// <param name"p1"></param> /// <param name"p2"></param> /// &l…

跨国企业如何选择安全靠谱的跨国传输文件软件?

随着全球化的不断发展&#xff0c;跨国企业之间的合作变得越来越频繁。而在这种合作中&#xff0c;如何安全、可靠地将文件传输给合作伙伴或客户&#xff0c;成为了跨国企业必须面对的问题。 然而&#xff0c;跨国文件传输并不是一件容易的事情&#xff0c;由于网络物理条件的…