C/C++ 哈夫曼树 创建 和 BFS遍历 算法

一、哈夫曼树概述

  • 哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。
  • 所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
  • 树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1L1+W2L2+W3L3+…+WnLn),N个权值Wi(i=1,2,…n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,…n)。
  • 哈夫曼树的WPL是最小的。


二、哈夫曼树的特性

哈夫曼树又称为最优树:

  • 路径和路径长度
    在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。

  • 结点的权及带权路径长度
    若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。

  • 树的带权路径长度
    树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
    带权二叉树

    哈夫曼树



三、构造哈夫曼树

定义哈夫曼树结构

typedef struct HuffmanTree {			//创建哈夫曼树结构;
	int weight;							//存放权值;
	int prt, lcd, rcd;					//定义父结点,左孩子,右孩子;
}HfNode, *HfTree;

创建哈夫曼树

void CreateHuffmanTree(HfTree &HT, int n)			//创建哈夫曼树;
{
	if (n <= 1) return;
	int m = 2 * n - 1;								//设置最大结点数;					
	HT = new HfNode[m + 1];							//动态分配m+1个结点单元,因为0号结点没有使用,所以+1;
	for (int i = 1; i <= m; i++)					//初始化所有结点的父结点、左孩子、右孩子为0;
	{
		HT[i].lcd = HT[i].rcd = HT[i].prt = 0;
	}
	cout << "请输入需要构造哈夫曼树的序列:";
	for (int i = 1; i <= n; i++)					//输入前n个结点的权值;
	{
		cin >> HT[i].weight;
	}
	for (int i = n + 1; i <= m; i++)				//通过n-1次操作来创建哈夫曼树;
	{
		int s1, s2;									//声明两个存放最小结点位置的变量;
		select(HT, s1, s2, i);						//调用select函数,将选择的最小值赋给s1、s2;
		HT[i].lcd = s1;
		HT[i].rcd = s2;								//将s1,s2所在位置的结点分别当作当前结点的左右孩子;
		HT[s1].prt = HT[s2].prt = i;				//更改s1,s2所在位置结点的父结点为当前结点;
		HT[i].weight = HT[s1].weight + HT[s2].weight;//当前结点的权值等于左右孩子权值之和;
	}
}

PS:在创建哈夫曼树的过程中有一个选择函数,必须从小到大选则,刚开始我是打算只遍历一遍找出最小的两个结点位置的,但是会出现很多错误,一直没能解决,最后还是用了遍历两次的方法。



四、BFS 遍历哈夫曼树

void BFS_Print(HfNode* HT, int m, int n)				//BFS输出哈夫曼树;
{
	int t = 0;
	int b[50] = { 0 };
	queue <int> q;
	q.push(m);
	cout << "        ";
	while (!q.empty())								//遍历队列;
	{
		if (b[q.front()] != t) {					//为了让输出显得更有层次;
			t += 1;
			cout << endl;
			for (int i = n-t; i >= 0; i--)
				cout << " ";
		}
		if (b[q.front()] == t)
			cout << HT[q.front()].weight << " ";
		/*判断该结点有左孩子和右孩子吗,有的话将其入队,没有的话置空*/
		HT[q.front()].lcd != 0 ? q.push(HT[q.front()].lcd) : NULL;
		HT[q.front()].rcd != 0 ? q.push(HT[q.front()].rcd) : NULL;
		b[HT[q.front()].lcd] = b[HT[q.front()].rcd] = t + 1;
		q.pop();									//记得将队首出队;
	}
	cout << endl;
}


五、完整代码

#include <iostream>
#include <queue>


using namespace std;

/*
8
5 29 7 8 14 23 3 11
*/

typedef struct HuffmanTree {			//创建哈夫曼树结构;
	int weight;							//存放权值;
	int prt, lcd, rcd;					//定义父结点,左孩子,右孩子;
}HfNode, *HfTree;

void select(HfTree &HT, int &s1, int &s2, int m)	//按顺序查找当前树结点中最小的两个结点
{
	int x = 0xcfffff;
	int y = x + 1;
	for (int i = 1; i < m; i++)
	{
		if (!HT[i].prt) {
			if (x > HT[i].weight) {
				x = HT[i].weight;
				s1 = i;
			}
		}
	}
	for (int i = 1; i < m; i++)
	{
		if (!HT[i].prt) {
			if (y > HT[i].weight &&  (HT[i].weight > x||HT[i].weight == x && i != s1)) {
				y = HT[i].weight;
				s2 = i;
			}
		}
	}
}

void BFS_Print(HfNode* HT, int m, int n)				//BFS输出哈夫曼树;
{
	int t = 0;
	int b[50] = { 0 };
	queue <int> q;
	q.push(m);
	cout << "        ";
	while (!q.empty())								//遍历队列;
	{
		if (b[q.front()] != t) {					//为了让输出显得更有层次;
			t += 1;
			cout << endl;
			for (int i = n-t; i >= 0; i--)
				cout << " ";
		}
		if (b[q.front()] == t)
			cout << HT[q.front()].weight << " ";
		/*判断该结点有左孩子和右孩子吗,有的话将其入队,没有的话置空*/
		HT[q.front()].lcd != 0 ? q.push(HT[q.front()].lcd) : NULL;
		HT[q.front()].rcd != 0 ? q.push(HT[q.front()].rcd) : NULL;
		b[HT[q.front()].lcd] = b[HT[q.front()].rcd] = t + 1;
		q.pop();									//记得将队首出队;
	}
	cout << endl;
}

void CreateHuffmanTree(HfTree &HT, int n)			//创建哈夫曼树;
{
	if (n <= 1) return;
	int m = 2 * n - 1;								//设置最大结点数;					
	HT = new HfNode[m + 1];							//动态分配m+1个结点单元,因为0号结点没有使用,所以+1;
	for (int i = 1; i <= m; i++)					//初始化所有结点的父结点、左孩子、右孩子为0;
	{
		HT[i].lcd = HT[i].rcd = HT[i].prt = 0;
	}
	cout << "请输入需要构造哈夫曼树的序列:";
	for (int i = 1; i <= n; i++)					//输入前n个结点的权值;
	{
		cin >> HT[i].weight;
	}
	for (int i = n + 1; i <= m; i++)				//通过n-1次操作来创建哈夫曼树;
	{
		int s1, s2;									//声明两个存放最小结点位置的变量;
		select(HT, s1, s2, i);						//调用select函数,将选择的最小值赋给s1、s2;
		HT[i].lcd = s1;
		HT[i].rcd = s2;								//将s1,s2所在位置的结点分别当作当前结点的左右孩子;
		HT[s1].prt = HT[s2].prt = i;				//更改s1,s2所在位置结点的父结点为当前结点;
		HT[i].weight = HT[s1].weight + HT[s2].weight;//当前结点的权值等于左右孩子权值之和;
	}
}

int main()
{
	int n;
	HfTree HT;

	cout << "输入叶子结点的个数 n:";
	cin >> n;
	CreateHuffmanTree(HT, n);				//创建哈夫曼树;
	BFS_Print(HT, 2*n-1, n);				//BFS输出哈夫曼树;

	return 0;
}


蒟蒻一只,欢迎指正

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值