C++题之赫夫曼编码及树综合

A. DS二叉树--赫夫曼树的构建与编码(不含代码框架)

题目描述

给定n个权值,根据这些权值构造huffman树,并进行huffman编码

参考课本P147算法6.12 HuffmanCoding代码,注意数组访问是从位置1开始

要求:赫夫曼的构建中,默认左孩子权值不大于右孩子权值

输入

第一行输入t,表示有t个测试实例
第二行先输入n,表示第1个实例有n个权值,接着输入n个权值,权值全是小于1万的正整数
依此类推

输出

逐行输出每个权值对应的编码,格式如下:权值-编码
即每行先输出1个权值,再输出一个短划线,再输出对应编码,接着下一行输入下一个权值和编码。
以此类推

输入样例:

1
5 15 4 4 3 2
 

输出样例:

15-1
4-010
4-011
3-001
2-000

#include <iostream>
using namespace std;

class HuffNode {
public:
	int weight;
	int parent;
	int leftchild;
	int rightchild;
};

class Huffman {
private:
	HuffNode* ht;
	string* hcode;
	int len;
	int n;


public:
	void createTree(int n,int wt[]) {
		len = 2 * n - 1;
		this->n = n;
		ht = new HuffNode[len+1];
		hcode = new string[n+1];
		for (int i =  1; i <= len; i++) {
			if (i <= n)
				ht[i].weight = wt[i];
			else
				ht[i].weight = 0;
			ht[i].parent = 0;
			ht[i].leftchild = 0;
			ht[i].rightchild = 0;
		}
		createTree();
	}
	void createTree() {
		for (int i = n + 1; i <= len; i++) {
			int wt1, wt2;
			select(ht, i - 1, wt1, wt2);
			ht[wt1].parent = i; ht[wt2].parent = i;
			ht[i].leftchild = wt1; ht[i].rightchild = wt2;
			ht[i].weight = ht[wt1].weight + ht[wt2].weight;
		}

	}
	void select(HuffNode *ht, int pos, int& wt1, int& wt2) {
		int min1=9999, min2=9999;
		for (int i = 1; i <= pos; i++) {
			if (min1 > ht[i].weight && !ht[i].parent) {
				min2 = min1;
				wt2 = wt1;		//2 3 1 
				min1 = ht[i].weight;
				wt1 = i;	//得到权值小的序号
			}
			else if(min2>ht[i].weight&&!ht[i].parent) {
				min2 = ht[i].weight;
				wt2 = i;
			}
		}
		
	}

	void Encode() {
		int c=0, p, start ;
		char* cd;
		cd = new char[n];
		cd[n - 1] = '\0';
		for (int i = 1; i <= n; i++) {
			start = n - 1;
			
			for (c = i, p = ht[i].parent; p != 0; c = p, p = ht[p].parent) {
		
				if (ht[p].leftchild == c)
					cd[--start] = '0';
				else
					cd[--start] = '1';

			}
			hcode[i].assign(&cd[start]);

		}
		delete[]cd;

	}
	void display() {
		for (int i = 1; i <= n; i++) {
			cout << ht[i].weight << '-';
			cout << hcode[i] << endl;
		}
	}
};



int main() {

	int t, n;
	cin >> t;
	while (t--) {
		cin >> n;
		int* wt = new int[n+1];
		for (int i =1; i <= n; i++) {
			cin >> wt[i];
		}

		Huffman huff;
		huff.createTree(n, wt);
		huff.Encode();
		huff.display();
	}

	return 0;
}

B. DS二叉树--赫夫曼树解码(不含代码框架)

题目描述

已知赫夫曼编码算法和程序,在此基础上进行赫夫曼解码

在赫夫曼树的类定义中增加了一个公有方法:

int  Decode(const string codestr, char txtstr[]); //输入编码串codestr,输出解码串txtstr

该方法如果解码成功则返回1,解码失败则返回-1,本程序增加宏定义ok表示1,error表示-1

输入

第一行输入t,表示有t个测试实例
第二行先输入n,表示第1个实例有n个权值,接着输入n个权值,权值全是小于1万的正整数
第三行输入n个字母,表示与权值对应的字符
第四行输入k,表示要输入k个编码串
第五行起输入k个编码串
以此类推输入下一个示例

输出

每行输出解码后的字符串,如果解码失败直接输出字符串“error”,不要输出部分解码结果

输入样例:

2
5 15 4 4 3 2
A B C D E
3
11111
10100001001
00000101100
4 7 5 2 4
A B C D
3
1010000
111011
111110111
 

输出样例:

AAAAA
ABEAD
error
BBAAA
error
DCD
 

#include <iostream>
using namespace std;

class HuffNode {
public:
	int weight;
	int latter;
	int parent;
	int lchild;
	int rchild;
};

class Huffman {
private:
	HuffNode* ht;
	string *hcode;
	int len;
	int n;
public:
	void createTree(int n,int wt[],char lt[]) {
		len = 2 * n - 1;
		this->n = n;
		ht = new HuffNode[len + 1];
		for (int i = 1; i <= len; i++) {
			if (i <= n) {
				ht[i].weight = wt[i];
				ht[i].latter = lt[i];
			}
			else {
				ht[i].weight = 0;
				ht[i].latter = 0;
			}			
			ht[i].parent = 0;
			ht[i].lchild = 0;
			ht[i].rchild = 0;
				
		}
		createTree();
	}
	void createTree() {
		for (int i = n + 1; i <= len; i++) {
			int wt1, wt2;
			select(ht, i - 1, wt1, wt2);
			ht[wt1].parent = i; ht[wt2].parent = i;
			ht[i].lchild = wt1; ht[i].rchild = wt2;
			ht[i].weight = ht[wt1].weight + ht[wt2].weight;
		}
	}
	void select(HuffNode* ht, int pos, int& wt1, int& wt2) {
		int min1 = 9999, min2 = 9999;
		for (int i = 1; i <= pos; i++) {
			if (min1 > ht[i].weight && !ht[i].parent) {
				min2 = min1;
				wt2 = wt1;
				min1 = ht[i].weight;
				wt1 = i;
			}
			else if (min2 > ht[i].weight && !ht[i].parent) {
				min2 = ht[i].weight;
				wt2 = i;
			}

		}
	}
	//编码
	void Encode() {
		int c, p, start;
		char* cd;
		cd = new char[n + 1];
		cd[n - 1] = '\0';
		hcode = new string[n + 1];
		for (int i = 1; i <= n; i++) {
			start = n - 1;
			for (c = i, p = ht[i].parent; p != 0; c = p, p = ht[p].parent) {
				if (ht[p].lchild == c) {
					cd[--start] = '0';
				}
				else
					cd[--start] = '1';
			}
			hcode[i].assign(&cd[start]);

		}
		delete[]cd;
	}
	//解码
	int Decode(string str,char decode[],int &size) {
		int length = str.size();
		int i = 0, k=0,pos;
		while (i < length) {
			int flag = 0;
			for (int j = 1; j <= n; j++) {	//遍历n个字母
				pos = str.find(hcode[j], i);	//从字符串str下标i的位置开始寻找有无编码里对应的字符串
				if (pos == i) {			//pos值返回的是寻找到的字符串在str中的下标位置
					flag = 1;			//寻找到置1
					decode[k] = ht[j].latter;	//解码存的是对应的字母
					k++;
					i += hcode[j].size();	//由于已经找到了这一小段编码对应的字母,下一次寻找是当前下标加上该段编码的长度的和开始

				}
			}
			if (flag == 0) {
				return -1;
			}
		}
		size = k;
		if (size != 0) {
			return 1;
		}
		else
			return -1;
	}

};


int main() {
	int t, n;
	cin >> t;
	while (t--) {
		cin >> n;
		int* wt = new int[n+1];
		for (int i = 1; i <= n; i++) {
			cin >> wt[i];
		}
		char* latter = new char[n+1];
		for (int i = 1; i <= n; i++) {
			cin >> latter[i];
		}
		Huffman huff;
		huff.createTree(n, wt,latter);
		huff.Encode();
		
		int k,size=0;
		string str;
		char *decode=new char[25];
		cin >> k;
		while (k--) {
			cin >> str;
			if (huff.Decode(str, decode,size) == -1) {
				cout << "error" << endl;
			}
			else {
				for (int i = 0; i < size; i++) {
					cout << decode[i];
				}
				cout << endl;
			}

		}

		delete[]wt;

	}

	return 0;
}

C. DS树--带权路径和

题目描述

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

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

树的带权路径和 = 7*1 + 6*2 + 2*3 + 3*3 = 34

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

输入

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

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

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

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

输出

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

输入样例:

2
xA00tB00zC00D00
4 7 6 2 3
ab0C00D00
2 10 20

输出样例:

34
40
 

#include <iostream>
using namespace std;

class BtNode {
public:
	char data;
	int weight;
	BtNode* lchild, *rchild;
};

class BiTree {
private:
	BtNode* root;	//根结点
	string tree;	//字符串存储二叉树的先序遍历结果
	int wt[50];		//叶子权值
	int len;		//结点数目
	int n;			//叶子数目
	int pos;
	int apl;		//apl值
	int k;			
public:
	//构建二叉树
	void createTree(int n,int _wt[],string _tree) {
		pos = 0;
		k = 1;
		apl = 0;
		this->n = n;
		len = n * 2 - 1;
		tree.assign(_tree);
		createTree(root, _wt);
	}
	void createTree(BtNode* &t,int wt[]) {
		char ch;
		ch = tree[pos++];
		t = new BtNode();
		if (ch != '0') {
			t->data = ch;
			if (ch >= 'A' && ch <= 'Z') {
				t->weight = wt[k++];
			}
			else
				t->weight = 0;
			createTree(t->lchild,wt);
			createTree(t->rchild, wt);

		}
		else {
			t = NULL;
		}

	}
	//计算带权路径和
	void countAPL() {
		countAPL(root, 0);

	}
	void countAPL(BtNode* &t,int depth) {
		if (t) {
			apl += depth * (t->weight);		//该结点深度乘以该结点的权值,对apl进行叠加求和
			depth++;
			countAPL(t->lchild,depth);		//利用递归算法
			countAPL(t->rchild, depth);
		}

	}
	void display() {
		cout << apl << endl;
	}

};


int main() {
	int t,n;
	string tree;
	
	cin >> t;
	while (t--) {
		cin >> tree;
		cin >> n;
		int* wt = new int[n + 1];
		for (int i = 1; i <= n; i++) {
			cin >> wt[i];
		}
		BiTree T;
		T.createTree(n, wt, tree);
		T.countAPL();
		T.display();

	}

	return 0;
}

D. 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

#include <iostream>
using namespace std;

class BtNode {
public:
	char data;
	int weight;
	BtNode* lchild, * rchild;
};

class BiTree {
private:
	BtNode* root;
	string tree;
	int wt[50];
	int len;
	int n;
	int pos;
	int max_apl;	//最大路径权值
	int k;
public:
	//构建二叉树
	void createTree(int n, int _wt[], string _tree) {
		pos = 0;
		k = 1;
		max_apl = 0;
		this->n = n;
		len = n * 2 - 1;
		tree.assign(_tree);
		createTree(root, _wt);
	}
	void createTree(BtNode*& t, int wt[]) {
		char ch;
		ch = tree[pos++];
		t = new BtNode();
		if (ch != '0') {
			t->data = ch;
			if (ch >= 'A' && ch <= 'Z') {
				t->weight = wt[k++];
			}
			else
				t->weight = 0;
			createTree(t->lchild, wt);
			createTree(t->rchild, wt);

		}
		else {
			t = NULL;
		}

	}
	//计算最大路径权值
	void countAPL() {
		countAPL(root,0);


	}
	void countAPL(BtNode*& t,int _apl) {
		int apl = _apl;		//该结点的双亲的路径权值
		if (t) {
			apl +=t->weight;	//加上该结点的权值
			countAPL(t->lchild,apl);	
			countAPL(t->rchild,apl);
			if (max_apl < apl) {		//如果最大路径权值小于该结点的路径权值,则更新最大值,此条语句其实是遇到叶子结点时才真正的有意义比较
				max_apl = apl;			//因为叶子结点的双亲的路径权值一定比叶子结点的小
			}
		}

	}
	void display() {
		cout << max_apl << endl;
	}

};


int main() {
	int t, n;
	string tree;

	cin >> t;
	while (t--) {
		cin >> tree;
		cin >> n;
		int* wt = new int[n + 1];
		for (int i = 1; i <= n; i++) {
			cin >> wt[i];
		}
		BiTree T;
		T.createTree(n, wt, tree);
		T.countAPL();
		T.display();

	}

	return 0;
}

E. 二叉树的中后序遍历构建及求叶子

题目描述

按中序遍历和后序遍历给出一棵二叉树,求这棵二叉树中叶子节点权值的最小值。

输入保证叶子节点的权值各不相同。

输入

测试数据有多组
对于每组测试数据,首先输入一个整数N (1 <= N <= 10000),代表二叉树有N个节点,接下来的一行输入这棵二叉树中序遍历的结果,最后一行输入这棵二叉树后序遍历的结果
输入一直处理到文件尾(EOF)

输出

对于每组测试数据,输出一个整数,代表二叉树中叶子节点权值最小值

输入样例:

7
3 2 1 4 5 7 6
3 1 2 5 6 7 4
8
7 8 11 3 5 16 12 18
8 3 11 7 16 18 12 5
1
255
255

输出样例:

1
3
255

#include <iostream>
using namespace std;

class BtNode {
public:
	int data;
	BtNode* lchild, * rchild;
};

class BiTree {
private:
	BtNode* root;
	int* in;	//中序遍历的结果
	int* post;	//后序遍历的结果
	int minw;	//叶子结点权值的最小值
public:
	//构建二叉树
	void createTree(int n) {
		in = new int[n + 1];
		post = new int[n + 1];
		for (int i = 1; i <= n; i++) {
			cin >> in[i];
		}
		for (int i = 1; i <= n; i++) {
			cin >> post[i];
		}
		minw = in[1];	//初始化最小值
		createTree(root, 1, n, n);

	}
	void createTree(BtNode* &t,int l,int r,int rt) {
		t = new BtNode();
		int newroot;
		if (l > r)		//左范围大于右范围
		{
			t = NULL;
			return;
		}
		for (int i = l; i <= r; i++) {		
			if (in[i] == post[rt]) {		//找到根结点
				newroot = i;		//存储中序遍历中根结点的下标
				break;
			}
		}
		t->data = post[rt];
		createTree(t->lchild, l, newroot - 1, rt - 1 - (r - newroot));//左子树的范围为1,到根结点下标-1,新后序根结点
		createTree(t->rchild, newroot + 1, r, rt - 1);		//右子树的范围为根结点下标+1,一直到r,新后序根结点是旧后续根结点-1
	
	}
	void find() {
		find(root);
		cout << minw << endl;
	}
	void find(BtNode* &t) {
		if (t) {
			if (!t->lchild && !t->rchild) {		//找到叶子结点,无左右子树即是
				if (minw > t->data)
					minw = t->data;
			}
			find(t->lchild);
			find(t->rchild);
		}
	}
	
};



int main() {
	int n;
	char in[50];
	char post[50];
	while (cin >> n) {
		BiTree T;
		T.createTree(n);
		T.find();
	}


	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值