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;
}