哈夫曼树的构建(详解顺序结构和链式结构)

这篇具有很好参考价值的文章主要介绍了哈夫曼树的构建(详解顺序结构和链式结构)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

关于树的一些基本知识这里就不再提了,如果不知道的小伙伴可以先去了解一下,我们直接进入正题。哈夫曼树是一种特殊的树。根据定义:哈夫曼树,又叫做最优树,是一种带权路径长度最小的树。哈夫曼树中没有度为1的节点(哈夫曼树也是二叉树),因此包含n个结点的哈夫曼树一共具有n个叶结点和n-1个度为2的中间结点(这里是根据二叉树的一些性质得出的),共计2*n-1个结点(这点很重要)

接下来,我们来说一说哈夫曼树的构建思想:

1、现有n个权值,每个权值对应一个结点,这些结点构成了一个森林,森林中的每棵树Ti都是二叉树,且都仅包含一个具有权值的根节点,左右子树都为空,双亲也为空。

2、从森林中选取根节点权值最小的两棵树Ti和Tj(两棵树根节点双亲都为空)进行合并,创建出一棵新的二叉树。新的二叉树根节点的权值为Ti和Tj两棵树根节点权值之和。新的二叉树双亲为空,左右结点分别为Ti和Tj(Ti和Tj的双亲即为新的二叉树)。

3、重复上述过程直到森林中只剩下一棵二叉树为止(判断依据为只有一个根节点双亲为空或者已经有了2*n-1个结点),这棵树就是哈夫曼树。

具体实现上,我们先来看一看顺序结构:

1、顺序结构

顺序结构就是创建一个结构体数组保存各个结点,对于新生成的结点添加到数组末尾,当有2*n-1个结点时,哈夫曼树的构建就完成了。我们构建的是一张完整的保存哈夫曼树的表,我们用哈夫曼编码的方式来检验构建是否正确。哈夫曼编码:规定哈夫曼树中的左分支为0,右分支为1(反之也可以),则由根结点到某叶结点所经过的分支对应的0或1组成的序列就是该叶结点对应的哈夫曼编码。

我们采用的测试用例为 10  15  12  3  4。其形成的哈夫曼树如下图所示:

哈夫曼树构建,数据结构,算法,数据结构,c++

程序构建的保存哈夫曼树的表如下图所示(这里我们规定,每次选取的两个最小值中,第一小的值放在左子树上,第二小的值放在右子树上):

编号 权重 双亲(编号) 左子树(编号) 右子树(编号)
1 10 7
2 15 8
3 12 8
4 3 6
5 4 6
6 7 7 4 5
7 17 9 6 1
8 27 9 3 2
9 44 7 8

程序源代码:

# include <iostream>
# include <string>
using namespace std;
typedef struct HTNode {
	int data;
	int parent, lchild, rchild;
}HTNode, * HufTree;
int main()
{
	int N;
	cout << "请输入元素个数:";
	cin >> N;
	cout << "请输入各元素的权职:";
	HufTree HT;
	HT = (HufTree)malloc(2 * N * sizeof(HTNode)); //哈夫曼树总结点有2*N-1个,动态分配2*N个空间
	for (int i = 1; i <= N; i++) {     //输入原始结点
		cin >> HT[i].data;
		HT[i].parent = -1;
		HT[i].lchild = -1;
		HT[i].rchild = -1;
	}
	//构建哈夫曼树
	for (int i = N + 1; i < 2 * N; i++) {  //哈夫曼树中共有2*N-1个结点,所以循环到2*N-1
		//寻找两个根节点权值最小的树
		int m1 = i - 1, m2 = i - 1;  //m1保存第一小的位置,m2保存第二小的位置
		int x1 = 1e9, x2 = 1e9;      //x1保存第一小的值,x2保存第二小的值
		for (int j = 1; j < i; j++) {      //从第一个结点到当前的最后一个结点,寻找两个权重最小的位置
			if (HT[j].parent == -1 && HT[j].data < x1) {  //符合条件的值,双亲必须为空
				x2 = x1;
				x1 = HT[j].data;
				m2 = m1;  //m2接替原m1,保存当前第二小的位置
				m1 = j;   //将当前最小值的位置赋给m1
			}
			else if (HT[j].parent == -1 && HT[j].data < x2) {
				x2 = HT[j].data;
				m2 = j;  
			}
		}
		//添加新树
		HT[m1].parent = i;  //添加双亲
		HT[m2].parent = i;  //添加双亲
		HT[i].data = HT[m1].data + HT[m2].data; //新加入的结点,权重为两个最小值的和
		HT[i].lchild = m1;  //将第一小的位置作为左子树
		HT[i].rchild = m2;  //将第二小的位置作为右子树
		HT[i].parent = -1;  //新结点的双亲为空
	}
	//依据哈夫曼树,打印输出各原始节点的编码
	string s;
	for (int i = 1; i <= N; i++) {
		int j = i;
		int p = HT[j].parent;
		while (p != -1) {
			if (j == HT[p].lchild) {
				s.append(1, '0');  //如果是双亲的左子树则编为0
			}
			else {
				s.append(1, '1');  //如果是双亲的右子树则编为1
			}
			j = p;
			p = HT[p].parent;
		}
		cout << "权重为" << HT[i].data << "的结点的编码为";
		for (int k = s.size() - 1; k >= 0; k--) { //倒序输出的才是正确的编码方式
			cout << s[k];
		}
		cout << endl;
		s.clear();
	}
	return 0;
}

运行结果:

请输入元素个数:5
请输入各元素的权职:10 15 12 3 4
权重为10的结点的编码为01
权重为15的结点的编码为11
权重为12的结点的编码为10
权重为3的结点的编码为000
权重为4的结点的编码为001

2、链式结构

链式结构和顺序结构的实现思想是相同的。区别在于链式结构以邻接表为基本数据类型。就是创建一个指针类型的数组,每一个指针都指向一个结点,对于新生成的结点添加到数组末尾,到2*n-1个结点,也就是最后一个结点时,这个结点上的二叉树就是要构建的哈夫曼树。这里,我们通过先序(二叉树的一种遍历方法)打印哈夫曼树来得到输出结果。

程序源代码:

# include <iostream>
# include <stdlib.h>
# define maxn 256
using namespace std;

typedef struct HTNode {
	int data;
	struct HTNode* lchild, * rchild, * parent;
}HTNode, * HufTree;

void PriPrint_Tree(HufTree T) //先序遍历
{
	if (T) {
		cout << T->data << " ";
		PriPrint_Tree(T->lchild);
		PriPrint_Tree(T->rchild);
	}
}

HufTree H[maxn]; //指针类型的数组
HufTree T;
int main()
{
	int N;
	cout << "请输入元素个数:";
	cin >> N;
	cout << "请输入各元素的权职:";
	for (int i = 1; i <= N; i++) {
		H[i] = (HufTree)malloc(sizeof(HTNode));
		cin >> H[i]->data;
		H[i]->lchild = NULL;
		H[i]->rchild = NULL;
		H[i]->parent = NULL;
	}
	for (int i = N + 1; i < 2 * N; i++) {  //哈夫曼树中共有2*N-1个结点,所以循环到2*N-1
		int m1 = i - 1, m2 = i - 1;  //m1保存第一小的位置,m2保存第二小的位置
		int x1 = 1e9, x2 = 1e9;      //x1保存第一小的值,x2保存第二小的值
		for (int j = 1; j < i; j++) {      //从第一个结点到当前的最后一个结点,寻找两个权重最小的位置
			if (H[j]->parent == NULL && H[j]->data < x1) {  //符合条件的值,双亲必须为空
				x2 = x1;
				x1 = H[j]->data;
				m2 = m1;  //m2接替原m1,保存当前第二小的位置
				m1 = j;   //将当前最小值的位置赋给m1
			}
			else if (H[j]->parent == NULL && H[j]->data < x2) { //大于等于第一小的值,小于第二小的值
				x2 = H[j]->data;
				m2 = j;
			}
		}
		H[i] = (HufTree)malloc(sizeof(HTNode)); //先给新结点开空间
		H[m1]->parent = H[i];  //添加双亲
		H[m2]->parent = H[i];  //添加双亲
		H[i]->data = H[m1]->data + H[m2]->data; //新加入的结点,权重为两个最小值的和
		H[i]->lchild = H[m1];  //将第一小的位置作为左子树
		H[i]->rchild = H[m2];  //将第二小的位置作为右子树
		H[i]->parent = NULL;  //新结点的双亲为空
	}
	T = H[2 * N - 1]; //最后一个结点上保存的即是完整的哈夫曼树
	PriPrint_Tree(T); //先序遍历打印哈夫曼树
	cout << endl;
	return 0;
}

运行结果:

请输入元素个数:5
请输入各元素的权职:10 15 12 3 4
44 17 7 3 4 10 27 12 1

以上是我个人的学习成果,很高兴能与大家分享。文章来源地址https://www.toymoban.com/news/detail-742570.html

到了这里,关于哈夫曼树的构建(详解顺序结构和链式结构)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 哈夫曼树的构造和哈夫曼编码实现详细讲解(含例题详细讲解)

    目录 一、哈夫曼树    1.哈夫曼树的基本概念    2.哈夫曼树的构造过程    3.哈夫曼树的的实现 二、哈夫曼编码     1.有关哈夫曼树编码的两个概念     2.哈夫曼树编码满足的两个性质     3.哈夫曼编码的实现 三、例题(含完整代码及详细注解)     1.题目     2.代码实现

    2024年02月07日
    浏览(51)
  • 哈夫曼树的构造及C++代码实现

    哈夫曼树的构造过程: (1) 以权值分别为W1,W2...Wn的n各结点,构成n棵二叉树T1,T2,...Tn并组成森林F={T1,T2,...Tn},其中每棵二叉树 Ti仅有一个权值为 Wi的根结点; (2) 在F中选取两棵根结点权值最小的树作为左右子树构造一棵新二叉树,并且置新二叉树根结点权值为

    2024年02月05日
    浏览(28)
  • 数据结构——哈夫曼树与哈夫曼编码

    1.1 基本概念 路径 :指从根结点到该结点的分支序列。 路径长度 :指根结点到该结点所经过的分支数目。 结点的带权路径长度 :从树根到某一结点的路径长度与该结点的权的乘积。 树的带权路径长度(WPL) :树中从根到所有叶子结点的各个带权路径长度之和。 哈夫曼树 是由

    2024年02月05日
    浏览(46)
  • 数据结构之哈夫曼树和哈夫曼编码

    切入正题之前,我们先了解几个概念: 路径:从树的一个结点到另一个结点分支所构成的路线 路径长度:路径上的分支数目 树的路径长度:从根结点出发到每个结点的路径长度之和 带权路径长度:该结点到根结点的路径长度乘以该结点的权值 树的带权路径长度:树中所有

    2024年02月11日
    浏览(46)
  • 数据结构之哈夫曼树与哈夫曼编码

    编码是信息处理的基础(重新表示信息)。 普通的编码是等长编码,例如7位的ASCIL编码,对出现频率不同的字符都使用相同的编码长度。 但其在传输和存储等情况下编码效率不高 。 可使用不等长编码,来压缩编码:高频字符编码长度更短,低频字符编码长度更长。   [例

    2023年04月15日
    浏览(64)
  • 哈夫曼树详解及其应用(哈夫曼编码)

    一,哈夫曼树的基本概念 路径: 从树中一个结点到另一个结点之间的 分支 构成这两个结点间的路径 结点的路径长度 :两结点之间路径上的 分支数 树的路径长度: 从 树根 到每一个结点的 路径长度之和 . 记作:TL 权(weight): 将树中结点赋给一个有着某种含义的数值

    2024年02月04日
    浏览(51)
  • 哈夫曼树,哈夫曼编码及解码详解

    🌍新人小白的博客 ⌛️希望大家多多关注 🌱一起加油,共同成长 🎃以后会经常更新哒~🙈 ⭐️个人主页: 收藏加关注,永远不迷路~⭐️ 一: 顺序表的操作,你真的学会了吗? 二: 顺序栈的基本操作 三: 循环队列的基本操作,你学会了吗? 四: 单链表的操作(超详细

    2024年02月05日
    浏览(46)
  • 数据结构课程实验五:哈夫曼树与哈夫曼编码

    实验日期:2022-12-20   目录 一、实验目的 1、掌握哈夫曼树的建立 2、掌握哈夫曼编码方式 二、实验内容

    2024年02月05日
    浏览(50)
  • 【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码

    超详细讲解哈夫曼树(Huffman Tree)以及哈夫曼编码的构造原理、方法,并用代码实现。 路径 :从树中一个结点到另一个结点之间的 分支 构成这两个结点间的路径。 结点的路径长度 :两结点间路径上的 分支数 。 树的路径长度: 从树根到每一个结点的路径长度之和。记作: TL  权

    2024年02月06日
    浏览(53)
  • 拿捏-哈夫曼树构建及编码生成(建议收藏)

    在认识哈夫曼树之前,你必须知道以下几个基本术语: 1、什么是路径? 在一棵树中,从一个结点往下可以达到的结点之间的通路,称为路径。 如图,从根结点A到叶子结点I的路径就是A-C-F-I 2、什么是路径长度? 某一路径所经过的“边”的数量,称为该路径的路径长度 如图

    2024年02月07日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包