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

文章目录

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

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

#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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChinaSoft 论坛巡礼|开源软件供应链论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

Go语言安装教程

【Go系列-1】-Go安装教程 环境提前准备 安装的时候可以选择自己的目录进行环境管理 E:\Z_Enviroment\Go创建文件夹&#xff1a; E:\Z_Enviroment\Go E:\Z_Enviroment\GoWorks E:\Z_Enviroment\GoWorks\bin E:\Z_Enviroment\GoWorks\pkg E:\Z_Enviroment\GoWorks\src环境变量…

OpenCV:图像噪点消除与滤波算法

人工智能的学习之路非常漫长&#xff0c;不少人因为学习路线不对或者学习内容不够专业而举步难行。不过别担心&#xff0c;我为大家整理了一份600多G的学习资源&#xff0c;基本上涵盖了人工智能学习的所有内容。点击下方链接,0元进群领取学习资源,让你的学习之路更加顺畅!记得…

测试架构师基础-进阶体系知识点、性能测试安全测试

一、Linux必备知识 linux作为现在最流行的软件环境系统&#xff0c;一定需要掌握&#xff0c;目前的招聘要求都需要有linux能力。 二、Shell脚本 掌握shell脚本&#xff0c;包括shell基础与应用、shell逻辑控制、shell逻辑函数等。 三、互联网程序原理 自动化必由之路&#…

pid调参(实验室新人入门)

安装keil&#xff1a;下载MDK-ARM http://t.csdnimg.cn/yYF7W芯片包&#xff1a; https://www.keil.arm.com/devices/stmicroelectronics-stm32f429aghx/features/ 调参软件&#xff1a; https://blog.csdn.net/weixin_63568691/article/details/133606043调参方法&#xff1a;…

CSRF 漏洞详解

CSRF 漏洞详解 文章目录 CSRF 漏洞详解漏洞描述漏洞原理漏洞场景漏洞评级漏洞危害漏洞验证漏洞利用漏洞防御典型案例 漏洞描述 CSRF&#xff08;Cross-Site Request Forgery&#xff09;漏洞是一种Web应用程序安全漏洞&#xff0c;它允许攻击者利用受害者的已认证会话来执行未…

纯c语言模拟栈和队列(初学必看)

一、栈(Stack) 1.栈的概念及其结构 栈是一种特殊的线性表&#xff0c;在栈这个结构里&#xff0c;越先存进去的数据越难取出来。 这个结构就像是一个只有一端有打开的容器&#xff0c;越先放进去的球越在底部&#xff0c;想要把底部的球拿出来&#xff0c;就必须先把前面的求…

Python实现WOA智能鲸鱼优化算法优化卷积神经网络分类模型(CNN分类算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 鲸鱼优化算法 (whale optimization algorithm,WOA)是 2016 年由澳大利亚格里菲斯大学的Mirjalili 等提…

快速验证微信小程序的AppId和AppSecret是否正确

解决方案说明 该验证方法是一种敏捷且高效的方式&#xff0c;特别适用于快速确认给定的 AppID 和 AppSecret 是否有效。在处理大量凭证或需要频繁验证的情况下&#xff0c;这种方法可以帮助您迅速而准确地完成验证过程。 特点 快速验证&#xff1a; 通过调用微信开放平台的接…

Selenium浏览器自动化测试框架简单介绍

selenium简介 介绍 Selenium [1] 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样。支持的浏览器包括IE&#xff08;7, 8, 9, 10, 11&#xff09;&#xff0c;Mozilla Firefox&#xff0c;Safari&#xff0c;Google …

Rust编程中的线程间通信

1.消息传递 为了实现消息传递并发&#xff0c;Rust 标准库提供了一个 信道&#xff08;channel&#xff09;实现。信道是一个通用编程概念&#xff0c;表示数据从一个线程发送到另一个线程。 可以将编程中的信道想象为一个水流的渠道&#xff0c;比如河流或小溪。如果你将诸如…

Qt执行带参sql

//准备执行的sql语句&#xff0c;此为带参的sql语句query.prepare("update employee set Name:Name, Gender:Gender,Height:Height,"" Birthday:Birthday, Mobile:Mobile, Province:Province,"" City:City, Department:Department, Education:Educati…

农场养殖管理系统软件开发方案

一、项目概述 农场养殖管理系统是一款针对农场养殖管理的软件&#xff0c;旨在提高农场养殖效率和管理水平。本方案将详细介绍该系统的开发流程&#xff0c;包括需求分析、系统设计、数据库设计、界面设计、系统测试和上线运营等方面。 二、需求分析 在开发农场养殖管理系统…

Socket网络编程

本文主要讲解Socket网络编程。 首先介绍socket&#xff0c;包括TCP和UDP通信过程&#xff1b;然后介绍常用的函数&#xff1b;最后编写client-server例子&#xff0c;并进行测试。 文章目录 Socket介绍TCP通信过程服务器端通信过程&#xff1a;客户端通信过程&#xff1a; UDP通…

数据结构线性表——栈

前言&#xff1a;哈喽小伙伴们&#xff0c;今天我们将一起进入数据结构线性表的第四篇章——栈的讲解&#xff0c;栈还是比较简单的哦&#xff0c;跟紧博主的思路&#xff0c;不要掉队哦。 目录 一.什么是栈 二.如何实现栈 三.栈的实现 栈的初始化 四.栈的操作 1.数据入栈…

基于JavaWeb+SSM+校园零售商城微信小程序系统的设计和实现

基于JavaWebSSM校园零售商城微信小程序系统的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应…

AYIT-ACM实验室发展历程

AYIT-ACM简介 ACM协会为你的梦想插上翅膀。 本院ACM协会成立于2012年 2008年开始小规模参加河南省竞赛 2014年成功实现金牌零突破 指导老师&#xff1a;孙高飞老师 安阳工学院计算机科学与信息工程学院ACM队是一支优秀的队伍&#xff0c;一支充满活力与激情的队伍&am…

【51单片机】之入门详解(一)

&#x1f4c3;博客主页&#xff1a; 小镇敲码人 &#x1f49e;热门专栏&#xff1a;C语言进阶 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f30f; 任尔江湖满血骨&#xff0c;我自踏雪寻梅香。 万千浮云遮…

MFC 简单绘图与文本编辑

目录 一.创建单文档项目 二.消息映射机制 三.WM_PAINT消息触发 四.CVIEW类 五.设备上下文 六.资源类和资源的关系 七.画线&#xff0c;矩形 八.画布 九.画笔 十.画刷 十一.利用TRACE打印日志 十二.文本编程 十三.ID号 十四.菜单栏 十五.菜单命令路由 十六.工具…