赫夫曼树(最优二叉树)

赫夫曼树介绍

概念
赫夫曼树是最优二叉树的别名,他通过每个叶子节点算权值和,取最小权值和的那棵树便是哈夫曼树。
如何实现
将所有数排成一个数组内,找到其质量(他的价值)最小的两个,将其合成一颗树,左边是最小的,右边是第二小的,然后将其质量相加便是这颗新树的质量。然后再次搜索,找到质量最小的两个,一直往复直到只剩下一棵树,那便是哈夫曼树。

代码如下:

#include<iostream>
#include<iomanip>//这是输出格式的头文件

#define bug(a) (cout<<"*"<<a<<endl)
#define bugg(a,b) (cout<<"*"<<a<<' '<<b<<endl)
#define buggg(a,b,c) (cout<<"*"<<a<<' '<<b<<' '<<c<<endl)
#define endl '\n'
#define MAX 0x3f3f3f
using namespace std;
typedef long long ll;

const int M = 1000;
int arr[M];
typedef struct node
{
	int wei;
	int fa, l, r;
	char* code;
}banode, * bitree;//创建这个数组的结构体指针方便运用

void mi(bitree& s, int n, int& a, int& b) {//将这个数组和已经运算到的长度(注:是会随着你的运算增大不是不变的)
	int i, j, k;//还有最小的两个值通过引用变量来带出
	for (i = 1; i < n; i++) {//这里不能等于n,因为n是我们要得到值的位置,现在还是没有值的
		if (s[i].fa == 0) {//找到第一个没父母节点的
			a = i;//将其坐标赋值给a
			break;
		}
	}
	for (i = 1; i < n; i++) {
		if (s[i].fa == 0 && s[i].wei < s[a].wei)a = i;//然后搜索最小的质量并将其坐标赋值给a
	}
	for (k = 1; k < n; k++) {
		if (s[k].fa == 0 && k != a) {//与之前一样找到第一个没父母节点的
			b = k;//并将其坐标赋值给b
			break;
		}
	}
	for (k = 1; k < n; k++) {
		if (s[k].fa == 0 && s[k].wei < s[b].wei && k != a)b = k;//找到除坐标等于a的外最小的位置
	}
}
void hfm(bitree& s, int arr[], int n) {//将要建立的哈夫曼树的数组和初始数组以及长度放进去
	int i;
	for (i = 1; i <= 2 * n - 1; i++) {//因为从一开始所以可以赋值为0
		s[i].fa = 0;
		s[i].l = 0;
		s[i].r = 0;
		s[i].wei = 0;
	}
	for (i = 1; i <= n; i++) {//输入质量
		s[i].wei = arr[i];
	}
	for (i = n + 1; i <= 2 * n - 1; i++) {//下面是哈夫曼树的判断之前讲了
		int mil, mir;
		mi(s, i, mil, mir);
		s[mil].fa = i;
		s[mir].fa = i;
		s[i].l = mil;
		s[i].r = mir;
		s[i].wei = s[mil].wei + s[mir].wei;
	}
}
void out(bitree s, const int t) {
	cout << "位置  质量  父母节点  左孩子  右孩子    赫夫曼编码" << endl;
	cout << left;
	for (int i = 1; i <= t; i++) {
		cout << setw(5) << i << " ";//这个节点所在位置
		cout << setw(7) << s[i].wei << " ";//输出其质量
		cout << setw(7) << s[i].fa << " ";//父母节点位置
		cout << setw(7) << s[i].l << " ";//左孩子位置
		cout << setw(10) << s[i].r ;//输出右孩子位置
		if (i <= (t + 1) / 2)for (int j = strlen(s[i].code) - 1; j >= 0; j--) {
			cout << s[i].code[j];
		}
		else cout << "无赫夫曼编码";
		cout << endl;
	}
}
void pre(bitree s,int n) {//将赫夫曼编码弄出来
	for (int i = 1; i <= n; i++) {
		int j = i,k=0;
		s[i].code = new char[n+1];
		while (s[j].fa) {
			if (s[s[j].fa].l==j) {如果父母节点的左孩子坐标等于这个坐标,那就是左孩子
				s[i].code[k++] = '0';
			}
			else s[i].code[k++] = '1';
			j = s[j].fa;
		}
		s[i].code[k] = '\0';
	}
}

int main()
{
	bitree s;
	int n, i;
	cout << "输入数组长度:" << endl;
	cin >> n;
	cout << "输入赫夫曼数的数组:" << endl;
	for (i = 1; i <= n; i++)
		cin >> arr[i];//下面的有2n-1个
	s = new banode[2 * n];//创建空间,记得多创一个因为我们这个是从1开始的
	hfm(s, arr, n);
	pre(s, n);
	out(s, 2 * n - 1);
	return 0;
}

测试数据:
8
5 29 7 8 14 23 3 11
输出:

输入数组长度:
8
输入赫夫曼数的数组:
5 29 7 8 14 23 3 11
位置  质量  父母节点  左孩子  右孩子    赫夫曼编码
1     5       9       0       0         0001
2     29      14      0       0         10
3     7       10      0       0         1110
4     8       10      0       0         1111
5     14      12      0       0         110
6     23      13      0       0         01
7     3       9       0       0         0000
8     11      11      0       0         001
9     8       11      7       1         无赫夫曼编码
10    15      12      3       4         无赫夫曼编码
11    19      13      9       8         无赫夫曼编码
12    29      14      5       10        无赫夫曼编码
13    42      15      11      6         无赫夫曼编码
14    58      15      2       12        无赫夫曼编码
15    100     0       13      14        无赫夫曼编码

给个链接------>我是在这学的

给道例题

题目打出来不好看就直接给链接了
---->摘果子(洛谷)
就是要我们求搬运的和的最小,就是权的和。
不过我一开始不是那么想的,我想的是除了根节点以外所有节点的和,结果答案是对的,相当于说明了这个也可以求出权值。
下面是代码(跟之前的代码基本没区别,但是为了记忆这边建议重新手敲一遍,不然忘贼快)

#include<iostream>
#include<iomanip>

#define bug(a) (cout<<"*"<<a<<endl)
#define bugg(a,b) (cout<<"*"<<a<<' '<<b<<endl)
#define buggg(a,b,c) (cout<<"*"<<a<<' '<<b<<' '<<c<<endl)
#define endl '\n'
#define MAX 0x3f3f3f
using namespace std;	
typedef long long ll;

const int M = 10005;
int arr[M];
typedef struct {
	int wei;
	int fa, l, r;
}tree,*bitree;

void mi(bitree s,int n,int &a,int &b) {
	int i,j;
	for (i = 1; i < n; i++) {
		if (s[i].fa == 0) {
			a = i;
			break;
		}
	}
	for (i = 1; i < n; i++) {
		if (s[i].fa == 0 && s[i].wei < s[a].wei)a = i;
	}
	for (j = 1; j < n; j++) {
		if (s[j].fa == 0 && j != a)b = j;
	}
	for (j = 1; j < n; j++) {
		if (s[j].fa == 0 && j != a && s[j].wei < s[b].wei)b = j;
	}
}
void hfm(bitree& s, int arr[], int n) {
	int i;
	for (i = 1; i <= n; i++) {
		s[i].wei = s[i].l = s[i].r = s[i].fa = 0;
	}
	for (i = 1; i <= n; i++)
		s[i].wei = arr[i];
	for (i = n + 1; i <= 2 * n - 1; i++) {
		int ml, mr;
		mi(s, i, ml, mr);
		s[ml].fa = i;
		s[mr]. fa = i;
		s[i].l = ml;
		s[i].r = mr;
		s[i].wei = s[ml].wei + s[mr].wei;
	}
}
int main()
{
	bitree s;
	int n, i,ans=0;
	cin >> n;
	for (i = 1; i <= n; i++) {
		cin >> arr[i];
		ans += arr[i];
	}
	s = new tree[2 * n];
	hfm(s, arr, n);
	for (i = n + 1; i < 2 * n - 1; i++) {
		ans += s[i].wei;
	}
	cout << ans << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我已经怒不可遏了!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值