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

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

#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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值