数据结构期末拯救计划:哈夫曼树与哈夫曼编码

1.哈夫曼树的思想:给出一个权值序列,在序列里选第一小和第二小的两个权值,以它们为左右子树构建一棵二叉树,从森林里面删除这两个树再把新树添加进去,直到只有一棵树为止

2.计算带权路径长度

【问题描述】 输入一串正整数,正整数之间用空格键分开,请建立一棵哈夫曼树,以输入的数字作为叶子节点,求这棵哈夫曼树的带权路径长度。

【输入形式】 首先输入正整数的个数n,然后是对应的n个正整数,正整数个数不超过10个

【输出形式】 输出创建的哈夫曼树的带权路径总长度

【样例输入】 

5

4 5 6 7 8

【样例输出】 

69

select函数,给定一个数组和寻找区间,以及两个需要传出的值(函数的返回值只有一个),当parent不是0,即这个节点是某个节点的孩子,跳过这次循环,然后进行判断和赋值,接下来解析creat,首先原始数组有n个元素,按照两个元素合成一棵树的思想,又需要n-1个节点,总共需要2*n-1个节点初始化,又因为范围原因,m赋值为2*n,首先初始化0到n-1个节点,以权重为依据初始化,再对n到2*n-1个节点进行普通初始化,然后对n到2*n-1个节点进行合并操作,就是将新树的权重改为两个最小值的权重之和,在更新新树的左右节点以及两个最小值的父节点,接下来是下一个函数,找到最后一棵树的位置,然后遍历树,再进行权重运算(也就是权重乘高度,从0开始),在下标为2*n-2的位置为树的根节点,进行遍历

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
typedef struct tree
{
	int weight;
	int parent;
	int left;
	int right;
}tree;
void select(tree* arr, int limit, int& min1, int& min2)
{
	min1 = -1;
	min2 = -1;
	int fmin = 999;
	int smin = 999;
	for (int i = 0; i < limit; i++)
	{
		if (arr[i].parent != -1)
		{
			continue;
		}
		else
		{
			if (arr[i].weight < fmin)
			{
				smin = fmin;
				min2 = min1;
				fmin = i;
				min1 = i;
			}
			else if (arr[i].weight < smin)
			{
				smin = arr[i].weight;
				min2 = i;
			}
		}
	}

}
void creat(tree* arr, int *data, int n)
{
	for (int i = 0; i < n; i++)
	{
		arr[i].parent = -1;
		arr[i].weight = data[i];
		arr[i].left = arr[i].right = -1;
	}
	int m = 2 * n-1;
	for (int i = n; i < m; i++)
	{
		arr[i].parent = -1;
		arr[i].weight = -1;
		arr[i].left = arr[i].right = -1;
	}
	for (int i = n; i < m; i++)
	{
		int min1;
		int min2;
		select(arr, i, min1, min2);
		arr[i].weight = arr[min1].weight + arr[min2].weight;
		arr[min1].parent = arr[min2].parent = i;
		arr[i].left = min1;
		arr[i].right = min2;
	}
	
}
int sum = 0;
void solve(tree* arr, int pos, int h)
{
	if (arr[pos].left != -1)
	{
		solve(arr, arr[pos].left, h + 1);
	}
	if ( arr[pos].right==-1 && arr[pos].left == -1)
	{
		sum += arr[pos].weight * h;
	}
	if (arr[pos].right!= -1)
	{
		solve(arr, arr[pos].right, h + 1);
	}
	
}
int main()
{
	int arr[100];
	int count = 0;
	cin >> count;
	for (int i = 0; i < count; i++)
	{
		cin >> arr[i];
	}
	tree point[100];
	creat(point,arr, count);
	solve(point, 2 * count-2, 0);
	cout << sum;
	return 0;
}

哈夫曼树与哈夫曼编码

【问题描述】读入n个字符所对应的权值,自底向上构造一棵哈夫曼树,自顶向下生成每一个字符对应的哈夫曼编码,并依次输出。另,求解某字符串的哈夫曼编码,求解某01序列的译码。

【输入形式】输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。第二行中有n个用空格隔开的正整数,分别表示n个字符的权值,依次按照abcd...的默认顺序给出。然后是某字符串和某01序列。

【输出形式】前n行,每行一个字符串,表示对应字符的哈夫曼编码。然后是某字符串的哈夫曼编码,某01序列的译码。

【注意】保证每次左子树比右子树的权值小;如出现相同权值的,则先出现的在左子树,即下标小的在左子树。

【样例输入】

8
5 29 7 8 14 23 3 11

aabchg

00011110111111001

【样例输出】

0001
10
1110
1111
110
01
0000
001

000100011011100010000

acdef

现在来解析两个函数,outpom为输出哈夫曼编码,limit为树根节点所在位置,pos为查找位置,从查找位置的父节点开始打印,如果pos是左节点打印0,是右节点打印1,然后向上继续查找(哈夫曼编码为从根节点到子节点的路径),由于以上原因,要反向打印,翻译函数解析如下:next为树根位置,输入哈夫曼编码,再遍历,寻找从根到给定节点的路径,如果为0则移向左孩子,如果为1则移向右孩子,如果已经移动到叶子节点则打印该点的字母编号,并将next回退到根节点位置

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
typedef struct tree
{
	int weight;
	int parent;
	int left;
	int right;
}tree;
void select(tree* arr, int limit, int& min1, int& min2)
{
	min1 = -1;
	min2 = -1;
	int fmin = 999;
	int smin = 999;
	for (int i = 0; i < limit; i++)
	{
		if (arr[i].parent != -1)
		{
			continue;
		}
		else
		{
			if (arr[i].weight < fmin)
			{
				smin = fmin;
				min2 = min1;
				fmin = i;
				min1 = i;
			}
			else if (arr[i].weight < smin)
			{
				smin = arr[i].weight;
				min2 = i;
			}
		}
	}

}
void creat(tree* arr, int *data, int n)
{
	for (int i = 0; i < n; i++)
	{
		arr[i].parent = -1;
		arr[i].weight = data[i];
		arr[i].left = arr[i].right = -1;
	}
	int m = 2 * n-1;
	for (int i = n; i < m; i++)
	{
		arr[i].parent = -1;
		arr[i].weight = -1;
		arr[i].left = arr[i].right = -1;
	}
	for (int i = n; i < m; i++)
	{
		int min1;
		int min2;
		select(arr, i, min1, min2);
		arr[i].weight = arr[min1].weight + arr[min2].weight;
		arr[min1].parent = arr[min2].parent = i;
		arr[i].left = min1;
		arr[i].right = min2;
	}
	
}
void outpom(tree* arr, int limit, int pos)
{
	int num[100];
	int count = 0;
	int p = arr[pos].parent;
	while (p != 0)
	{
		if (pos == arr[p].left)
		{
			num[count++] = 0;
		}
		if (pos == arr[p].right)
		{
			num[count++] = 1;
		}
		pos = p;
		p = arr[pos].parent;
	}
	while (count--)
	{
		cout << num[count];
	}
}
void inpom(tree* arr, int limit)
{
	string code;
	cin >> code;
	int pos = 0;
	int next = limit;
	while (pos < code.size())
	{
		if (code[pos] == '0')
		{
			next = arr[next].left;
		}
		else if (code[pos] == '1')
		{
			next = arr[next].right;
		}
		if (arr[next].left == -1 && arr[next].right == -1)
		{
			char c = 'a';
			c = c + next; 
			cout << c;
			next = limit;
		}
		pos++;
	}
}
int main()
{
	int leave;
	cin >> leave;
	int w[100];
	int i = 0;
	for (i = 0; i < leave ; i++)
	{
		cin >> w[i]; 
	}
	tree* arr;
	creat(arr, w, leave);
	for (i = 0; i < leave; i++)
	{
		outpom(arr, 2 * leave - 2, i);
		cout << endl;
	}
	char s[20];
	cin >> s;
	for (i = 0; s[i] != '\0'; i++)
	{
		int ques = s[i] - 97 + 1;
		outpom(arr, 2 * leave - 2, ques);
	}
	cout << endl;
	inpom(arr, 2 * leave - 2);
	return 0;
}

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值