深圳大学计软《数据结构》实验08--赫夫曼数及其应用

问题 A: DS二叉树——Huffman编码与解码(不含代码框架)

题目描述

1、问题描述

给定n个字符及其对应的权值,构造Huffman树,并进行huffman编码和译(解)码。

构造Huffman树时,要求左子树根的权值小于、等于右子树根的权值。

进行Huffman编码时,假定Huffman树的左分支上编码为‘0’,右分支上编码为‘1’。

2、算法

构造Huffman树算法:

⑴ 根据给定的n个权值(w1, w2, …, wn)构成n棵二叉树的集合F={T1, T2, …, Tn},其中每棵二叉树Ti中只有一个权值为wi的根结点。

⑵ 在F中选取两棵根结点的权值最小的树,作为左、右子树构造一棵新的二叉树,且置其根结点的权值为其左、右子树权值之和。

⑶ 在F中删除这两棵树,同时将新得到的二叉树加入F中。

(4) 重复⑵, ⑶,直到F只含一棵树为止。

3、Huffman编码算法:

⑴ 从Huffman树的每一个叶子结点开始。

⑵ 依次沿结点到根的路径,判断该结点是父亲结点的左孩子还是右孩子,如果是左孩子则得到编码‘0’,否则得到编码‘1’,先得到的编码放在后面。

⑶ 直到到达根结点,编码序列即为该叶子结点对应的Huffman编码。

4、Huffman译(解)码算法:

⑴ 指针指向Huffman树的根结点,取第一个Huffman码。

⑵ 如果Huffman码为‘0’,将指针指向当前结点的左子树的根结点;如果Huffman码为‘1’,将指针指向当前结点的右子树的根结点。

⑶ 如果指针指向的当前结点为叶子结点,则输出叶子结点对应的字符;否则,取下一个Huffman码,并返回⑵。

⑷ 如果Huffman码序列未结束,则返回⑴继续译码。

输入

第一行测试次数

第2行:第一组测试数据的字符个数n,后跟n个字符

第3行:第一组测试数据的字符权重

待编码的字符串s1

编码串s2

其它组测试数据类推

输出

第一行~第n行,第一组测试数据各字符编码值

第n+1行,串s1的编码值

第n+2行,串s2的解码值,若解码不成功,输出error!

其它组测试数据类推

样例输入

2
5 A B C D E
15 4 4 3 2
ABDEC
00000101100
4 A B C D
7 5 2 4
ABAD
1110110

样例输出

A :1
B :010
C :011
D :001
E :000
1010001000011
error!
A :0
B :10
C :110
D :111
0100111
DAC

AC代码

#include<iostream>
#include<algorithm>
using namespace std;

struct Ha_TNode {
	int parent, left, right, weight;
	string code;
	char data;
	Ha_TNode() { parent = 0; left = 0; right = 0; }
};

class HTree {
	Ha_TNode* root;
	int num;
public:
	HTree(int n = 0) {
		num = n;
		root = new Ha_TNode[2 * n];

		for (int i = 1; i <= n; i++) 
			cin >> root[i].data;
		
		for (int i = 1; i <= n; i++) 
			cin >> root[i].weight;
		
	}
	void selectMin(int len, int& p1, int& p2) {
		int _max , min1, min2;
		_max = min1 = min2 = 10000;
		p1 = p2 = 0;
		for (int i = 1; i < len; i++) {
			if (root[i].parent == 0)
				if (root[i].weight < min1) {
					min2 = min1;
					p2 = p1;
					min1 = root[i].weight;
					p1 = i;
				}
				else if (root[i].weight < min2) {
					min2 = root[i].weight;
					p2 = i;
				}
		}

	}

	void create_tree() {
		int p1 = 0, p2 = 0;
		for (int i = num + 1; i < 2 * num; i++) {
			selectMin(i, p1, p2);
			root[i].left = p1;
			root[i].right = p2;
			root[p1].parent = root[p2].parent = i;
			root[i].weight = root[p1].weight + root[p2].weight;
		}
	}

	void h_code() {
		for (int i = 1; i <= num; i++) {
			for (int c = i, f = root[i].parent; f != 0; c = f, f = root[f].parent)
				if (root[f].left == c)
					(root + i)->code = '0' + (root + i)->code;
				else
					(root + i)->code = '1' + (root + i)->code;
		}
	}

	void Code(string str) {
		for (int i = 1; i <= num; i++)
			cout << (root + i)->data << " :" << (root + i)->code << endl;
		for (int i = 0; i < str.length(); i++)
			for (int j = 1; j <= num; j++)
				if (str[i] == root[j].data) {
					cout << root[j].code;
					break;
				}
		cout << endl;
	}

	void de_code(string str) {
		int Root = 2 * num - 1;
		char ch;
		int c = Root;
		string ans;

		for (int i = 0; i < str.length(); i++) {
			ch = str[i];
			if (ch == '0')
				c = root[c].left;
			else if (ch == '1')
				c = root[c].right;
			else
			{
				cout << "error!" << endl;
				break;
			}

			if ((root[c].left == 0) && (root[c].right == 0)) {
				ans += root[c].data;
				c = Root;
			}
			else
				ch = '\0';
		}
		if (ch == '\0')
			cout << "error!" << endl;
		else
			cout << ans << endl;
	}

};
int main() {
	int t;
	cin >> t;
	while (t--) {
		int n;
		cin >> n;
		HTree tree(n);
		tree.create_tree();
		tree.h_code();
		string code;
		cin >> code;
		tree.Code(code);
		cin >> code;
		tree.de_code(code);
	}
	return 0;
}

问题 B: DS树–二叉树高度

题目描述

给出一棵二叉树,求它的高度。二叉树的创建采用前面实验的方法。

注意,二叉树的层数是从1开始

输入

第一行输入一个整数t,表示有t个二叉树

第二行起输入每个二叉树的先序遍历结果,空树用字符‘0’表示,连续输入t行

输出

每行输出一个二叉树的高度

样例输入

1
AB0C00D00

样例输出

3

AC代码

#include<bits/stdc++.h>
using namespace std;

struct BTree_node
{
	char date;
	BTree_node* left, * right;
	void set(char c, BTree_node* l = NULL, BTree_node* r = NULL) { date = c; left = l; right = r; }
};

class BTree {
	BTree_node* root;
	void preorder_tra(BTree_node* node) {
		if (!node)
			return;
		cout << node->date;
		preorder_tra(node->left);
		preorder_tra(node->right);
	}
	void inorder_tra(BTree_node* node)
	{
		if (!node)
			return;
		inorder_tra(node->left);
		cout << node->date;
		inorder_tra(node->right);
	}
	void postorder_tra(BTree_node* node)
	{
		if (!node)
			return;
		postorder_tra(node->left);
		postorder_tra(node->right);
		cout << node->date;
	}
	void createTree(BTree_node*& node) {
		char ch;
		cin >> ch;
		if ('0' == ch) {
			node = NULL;
			return;
		}
		node = new BTree_node;
		node->set(ch);
		createTree(node->left);
		createTree(node->right);
	}
	void del_tree(BTree_node* node) {
		if (!node) {
			delete node;
			return;
		}
		del_tree(node->left);
		del_tree(node->right);
		delete node;
	}
	int get_depth(BTree_node* node) {
		if (node == NULL)
			return 0;
		int left_depth = get_depth(node->left);
		int right_depth = get_depth(node->right);
		return 1 + (left_depth > right_depth ? left_depth : right_depth);
	}
public:
	BTree() { root = NULL; }
	void createTree() { createTree(root); }
	void preorder_tra() { preorder_tra(root); cout << endl; }
	void inorder_tra() { inorder_tra(root); cout << endl; }
	void postorder_tra() { postorder_tra(root); cout << endl; }
	~BTree() { del_tree(root); }
	int get_depth() { return get_depth(root); }
};


int main() {
	int n;
	cin >> n;
	while (n--) {
		BTree b;
		b.createTree();
		cout << b.get_depth() << endl;
	}
	return 0;
}

问题 C: DS树–二叉树之最大路径

题目描述

给定一颗二叉树的逻辑结构(先序遍历的结果,空树用字符‘0’表示,例如AB0C00D00),建立该二叉树的二叉链式存储结构

二叉树的每个结点都有一个权值,从根结点到每个叶子结点将形成一条路径,每条路径的权值等于路径上所有结点的权值和。编程求出二叉树的最大路径权值。如下图所示,共有4个叶子即有4条路径,

路径1权值=5 + 4 + 11 + 7 = 27 路径2权值=5 + 4 + 11 + 2 = 22

路径3权值=5 + 8 + 13 = 26 路径4权值=5 + 8 + 4 + 1 = 18

可计算出最大路径权值是27。

该树输入的先序遍历结果为ABCD00E000FG00H0I00,各结点权值为:

A-5,B-4,C-11,D-7,E-2,F-8,G-13,H-4,I-1

在这里插入图片描述

输入

第一行输入一个整数t,表示有t个测试数据

第二行输入一棵二叉树的先序遍历,每个结点用字母表示

第三行先输入n表示二叉树的结点数量,然后输入每个结点的权值,权值顺序与前面结点输入顺序对应

以此类推输入下一棵二叉树

输出

每行输出每棵二叉树的最大路径权值,如果最大路径权值有重复,只输出1个

样例输入

2
AB0C00D00
4 5 3 2 6
ABCD00E000FG00H0I00
9 5 4 11 7 2 8 13 4 1

样例输出

11
27

AC代码

#include<bits/stdc++.h>
using namespace std;

struct BTree_node
{
	char date;
	BTree_node* left, * right;
	int weight;
	void set(char c, BTree_node* l = NULL, BTree_node* r = NULL, int w = 0) { date = c; left = l; right = r; weight = 1; }
};

class BTree {
	BTree_node* root;
	int max_path(BTree_node*node) {
		if (!node)
			return 0;
		int left_max_path = max_path(node->left);
		int right_max_path = max_path(node->right);
		return node->weight + (left_max_path > right_max_path ? left_max_path : right_max_path);
	}
	void input_weight(BTree_node*node){
		if (!node)
			return;
		cin >> node->weight;
		input_weight(node->left);
		input_weight(node->right);
	}
	void preorder_tra(BTree_node* node) {
		if (!node)
			return;
		cout << node->date;
		preorder_tra(node->left);
		preorder_tra(node->right);
	}
	void inorder_tra(BTree_node* node)
	{
		if (!node)
			return;
		inorder_tra(node->left);
		cout << node->date;
		inorder_tra(node->right);
	}
	void postorder_tra(BTree_node* node)
	{
		if (!node)
			return;
		postorder_tra(node->left);
		postorder_tra(node->right);
		cout << node->date;
	}
	void createTree(BTree_node*& node) {
		char ch;
		cin >> ch;
		if ('0' == ch) {
			node = NULL;
			return;
		}
		node = new BTree_node;
		node->set(ch);
		createTree(node->left);
		createTree(node->right);
	}
	void del_tree(BTree_node* node) {
		if (!node) {
			delete node;
			return;
		}
		del_tree(node->left);
		del_tree(node->right);
		delete node;
	}
	int get_depth(BTree_node* node) {
		if (node == NULL)
			return 0;
		int left_depth = get_depth(node->left);
		int right_depth = get_depth(node->right);
		return 1 + (left_depth > right_depth ? left_depth : right_depth);
	}
	
public:
	BTree() { root = NULL; }
	void createTree() { createTree(root); }
	void preorder_tra() { preorder_tra(root); cout << endl; }
	void inorder_tra() { inorder_tra(root); cout << endl; }
	void postorder_tra() { postorder_tra(root); cout << endl; }
	~BTree() { del_tree(root); }
	int get_depth() { return get_depth(root); }
	void input_weight() { int n; cin >> n; input_weight(root); }
	int max_path() { return max_path(root); }
};


int main() {
	int n;
	cin >> n;
	while (n--) {
		BTree b;
		b.createTree();
		b.input_weight();
		//b.postorder_tra();
		//b.input_weight();
		cout << b.max_path() << endl;
		//cout << b.get_depth() << endl;

	}
	return 0;
}

问题 D: DS树–带权路径和

题目描述

计算一棵二叉树的带权路径总和,即求赫夫曼树的带权路径和。

已知一棵二叉树的叶子权值,该二叉树的带权案路径和APL等于叶子权值乘于根节点到叶子的分支数,然后求总和。如下图中,叶子都用大写字母表示,权值对应为:A-7,B-6,C-2,D-3

树的带权路径和 = 71 + 62 + 23 + 33 = 34

在这里插入图片描述

本题二叉树的创建参考前面的方法

输入

第一行输入一个整数t,表示有t个二叉树

第二行输入一棵二叉树的先序遍历结果,空树用字符‘0’表示,注意输入全是英文字母和0,其中大写字母表示叶子

第三行先输入n表示有n个叶子,接着输入n个数据表示n个叶子的权值,权值的顺序和前面输入的大写字母顺序对应

以此类推输入下一棵二叉树

输出

输出每一棵二叉树的带权路径和

样例输入

2
xA00tB00zC00D00
4 7 6 2 3
ab0C00D00
2 10 20

样例输出

34
40

AC代码

#include<bits/stdc++.h>
using namespace std;

struct BTree_node
{
	char date;
	BTree_node* left, * right;
	int weight;
	void set(char c, BTree_node* l = NULL, BTree_node* r = NULL, int w = 0) { date = c; left = l; right = r; weight = 1; depth = 0; }
	int depth;
};

class BTree {
	BTree_node* root;
	int max_path(BTree_node* node) {
		if (!node)
			return 0;
		int left_max_path = max_path(node->left);
		int right_max_path = max_path(node->right);
		return node->weight + (left_max_path > right_max_path ? left_max_path : right_max_path);
	}
	void input_weight(BTree_node* node) {
		if (!node)
			return;
		if (!node->left && !node->right)
			cin >> node->weight;
		input_weight(node->left);
		input_weight(node->right);
	}
	void preorder_tra(BTree_node* node) {
		if (!node)
			return;
		cout << node->date;
		preorder_tra(node->left);
		preorder_tra(node->right);
	}
	void inorder_tra(BTree_node* node)
	{
		if (!node)
			return;
		inorder_tra(node->left);
		cout << node->date;
		inorder_tra(node->right);
	}
	void postorder_tra(BTree_node* node)
	{
		if (!node)
			return;
		postorder_tra(node->left);
		postorder_tra(node->right);
		cout << node->date;
	}
	void createTree(BTree_node*& node, int depth = 0) {
		char ch;
		cin >> ch;
		//cout << "#----  " << ch << endl;
		if ('0' == ch) {
			node = NULL;
			return;
		}
		node = new BTree_node;
		node->set(ch);
		node->depth = depth;
		createTree(node->left, depth + 1);
		createTree(node->right, depth + 1);
	}
	void del_tree(BTree_node* node) {
		if (!node) {
			delete node;
			return;
		}
		del_tree(node->left);
		del_tree(node->right);
		delete node;
	}
	int get_depth(BTree_node* node) {
		if (node == NULL)
			return 0;
		int left_depth = get_depth(node->left);
		int right_depth = get_depth(node->right);
		return 1 + (left_depth > right_depth ? left_depth : right_depth);
	}

public:
	BTree() { root = NULL; }
	void createTree() { createTree(root); }
	void preorder_tra() { preorder_tra(root); cout << endl; }
	void inorder_tra() { inorder_tra(root); cout << endl; }
	void postorder_tra() { postorder_tra(root); cout << endl; }
	~BTree() { del_tree(root); }
	int get_depth() { return get_depth(root); }
	void input_weight() { int n; cin >> n; input_weight(root); }
	int max_path() { return max_path(root); }

	int count_path() {
		int cnt = 0;
		stack<BTree_node*>s;
		s.push(root);
		while (!s.empty()) {
			BTree_node* cur_node = s.top();
			s.pop();
			if (!cur_node)
				continue;
			s.push(cur_node->left);
			s.push(cur_node->right);

			if (!cur_node->left && !cur_node->right)
				cnt += cur_node->depth * cur_node->weight;
		}
		return cnt;
	}
};


int main() {
	int n;
	cin >> n;
	while (n--) {
		BTree b;
		b.createTree();
		b.input_weight();
		//b.postorder_tra();
		//b.input_weight();
		//cout << b.max_path() << endl;
		cout << b.count_path() << endl;
		//cout << b.get_depth() << endl;

	}
	return 0;
}

问题 E: DS森林叶子编码

题目描述

给定一组森林,编写程序生成对应的二叉树,输出这颗二叉树叶结点对应的二进制编码.规定二叉树的左边由0表示,二叉树的右边由1表示。

输入

输入:

N B 表示N个树,每结点最多B个分支

第2行至第N+1行,每个树的先序遍历

输出

每行表示一个叶结点对应的二进制编码.

样例输入

3 3
A B 0 0 0 C 0 0 0 D 0 0 0
E F 0 0 0 0 0
G H 0 0 0 I J 0 0 0 0 0 0

样例输出

0 1 1
1 0
1 1 0 1 0

说明

当时是2020年11月9日,这题我没有做。请读者自行求解并留言。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

曹无悔

请支持我的梦想!

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

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

打赏作者

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

抵扣说明:

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

余额充值