C++ 哈夫曼树,哈夫曼编码
一、题目要求:
给26个大写英文字母设计哈夫曼编码,使总编码长度最短。
输入一段由26个大写英文字母组成的字符串,输出其哈夫曼编码。
输入一段哈夫曼编码,输出其对应的由26个英文字母组成的字符串。
二、代码实现:
1、HufTree.h
文件:
#ifndef HUFTREE_H
#define HUFTREE_H
#include <iostream>
#include <iomanip>
using namespace std;
char charlist[28] = {'0',
'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X',
'Y', 'Z'};
double weightlist[27] = {26.0,
0.0856,0.0139,0.0297,0.0378,
0.1304,0.0289,0.0199,0.0528,
0.0627,0.0013,0.0042,0.0339,
0.0249,0.0707,0.0797,0.0199,
0.0012,0.0677,0.0607,0.1045,
0.0249,0.0092,0.0149,0.0017,
0.0199,0.0008};
class HufNode
{
public:
char ch; //结点字符
double weight; //结点权值
int parent, lchild, rchild; //结点的父、左孩子、右孩子下标
};
class HufTree
{
public:
static void Create_HufTree(HufNode*&, int); //创建哈夫曼树
static void Travel_HufTree(HufNode*); //遍历哈夫曼树
static void Create_HufCode(HufNode*, char**&, int); //根据哈夫曼树求哈夫曼编码
};
void Select(HufNode*& nt, int m, int& s1, int& s2) //在 1 ~ i - 1 中选择两个无父结点且权值最小的结点,返回下标s1,s2
{
double minum1 = 10, minum2 = 10;
for(int i = 1; i <= m; i++)
{
if(nt[i].weight < minum1 && nt[i].parent == 0)
{
minum1 = nt[i].weight;
s1 = i;
}
}
for(int j = 1; j <= m; j++)
{
if(nt[j].weight < minum2 && nt[j].parent == 0 && j != s1)
{
minum2 = nt[j].weight;
s2 = j;
}
}
}
void HufTree::Create_HufTree(HufNode*& nt, int n = 26) //创建哈夫曼树
{
//存储初始化
if(n <= 1) return;
nt = new HufNode[2 * n]; //0号单元存储哈夫曼树的长度,故数组大小为 2 * n
nt[0].weight = 2 * n - 1; nt[0].ch = '0'; nt[0].parent = 0; nt[0].lchild = 0; nt[0].rchild = 0;
for(int i = 1; i <= 2 * n - 1; i++) //所有单元全部初始化为0
{ nt[i].ch = '0'; nt[i].weight = 0; nt[i].parent = 0; nt[i].lchild = 0; nt[i].rchild = 0; }
for(int j = 1; j <= n; j++)
{
nt[j].ch = charlist[j];
nt[j].weight = weightlist[j]; //前n个单元存储叶子结点
}
//创建哈夫曼树
int s1 = 0, s2 = 0;
for(int i = n + 1; i <= 2 * n - 1; i++) //通过n - 1次的选择、删除、合并来创建哈夫曼树
{
Select(nt, i - 1, s1, s2); //在 1 ~ i - 1 中选择两个无父结点且权值最小的结点,返回下标s1,s2
nt[s1].parent = i; nt[s2].parent = i; //将s1和s2的父结点改为i,也即删除s1,s2
nt[i].lchild = s1; nt[i].rchild = s2; //s1和s2分别作为左右孩子
nt[i].weight = nt[s1].weight + nt[s2].weight; //新结点权值为s1,s2权值之和
}
}
void HufTree::Travel_HufTree(HufNode* nt) //遍历哈夫曼树
{
cout << "下标" << " " << "字符" << " " << "weight" << " " << "parent" << " " << "lchild" << " " << "rchild" << endl;
for(int i = 1; i <= nt[0].weight; i++)
cout
<< std::left << setw(8) << i
<< std::left << setw(8) << nt[i].ch
<< std::left << setw(8) << nt[i].weight
<< std::right << setw(8) << nt[i].parent
<< std::right << setw(8) << nt[i].lchild
<< std::right << setw(8) << nt[i].rchild
<< endl;
}
void HufTree::Create_HufCode(HufNode* nt, char**& hclist, int n = 26) //根据哈夫曼树求哈夫曼编码
{
hclist = new char*[n + 1]; //分配存储 n 个字符编码的编码表(指针数组)空间,0单元不用,故指针数组大小为 n + 1
char *hc = new char[n]; //分配临时存放每个字符编码的数组空间,字符编码长度一定小于 n
for(int i = 1; i <= n; i++) //逐个字符求哈夫曼编码
{
hc[n - 1] = '\0'; //数组末尾为编码结束符
int start = n - 1; //start初始位置为数组末尾(编码结束符)
int c = i, f = nt[i].parent; // c 初始为当前待编码字符 i ,f 用于记录 i 的父结点下标
while(f != 0) //从叶子结点向上回溯,直到根结点
{
start--;
if(nt[f].lchild == c) //如果 c 是 f 的左孩子,则编码为0
hc[start] = '0';
else //反之编码为1
hc[start] = '1';
c = f; //向上回溯
f = nt[f].parent;
}
hclist[i] = new char[n - start]; //为第 i 个字符的编码分配空间
for(int j = 0; j < n - start; j++) //将求得的编码从临时空间 hc 复制到 hclist[i] 中
hclist[i][j] = hc[j + start];
}
delete [] hc; //释放临时空间
}
#endif // HUFTREE_H
2、main.cpp
文件:
#include <iostream>
#include <iomanip>
#include "HufTree.h"
using namespace std;
int main()
{
HufNode *h = nullptr;
char **hclist = nullptr;
HufTree::Create_HufTree(h);
HufTree::Travel_HufTree(h);
HufTree::Create_HufCode(h, hclist);
for(int i = 1; i <= 26; i++)
{
cout << charlist[i] << ' ';
for(int j = 0; hclist[i][j] != '\0'; j++)
{
cout << hclist[i][j] << ' ';
}
cout << endl;
}
cout << "请输入一串由‘A’~‘Z’构成的字符:";
string s_ch; getline(cin, s_ch);
cout << "该串字符的哈夫曼编码为:";
for(int i = 0; s_ch[i] != '\0'; i++) //遍历字符串的每一个字符
{
for(int j = 1; j <= 26; j++)
{
if(s_ch[i] == charlist[j]) //如果在字母表中找到匹配的字符
{
for(int k = 0; hclist[j][k] != '\0'; k++) //输出该字符对应的哈夫曼编码
cout << hclist[j][k] << ' ';
}
}
}
cout << endl << "请输入一串由‘0’和‘1’构成的编码:";
string s_code; getline(cin, s_code);
int flag, dex, pos = 0;
cout << "该串哈夫曼编码所对应的字符为:";
while(pos < s_code.size()) //遍历哈夫曼编码的每一段编码
{
for(int i = 1; i <= 26; i++) //遍历字母表
{
flag = 1; dex = i; //先假定当前字符与这段哈夫曼编码匹配
for(int j = 0; hclist[i][j] != '\0'; j++)
{
if(hclist[i][j] != s_code[pos + j]) //如果不匹配则flag置为0,dex置为-1
{
flag = 0;
dex = -1;
break;
}
}
if(flag == 1) //如果匹配则输出该字符,且pos向后移动,移动的长度为:该字符所对应的哈夫曼编码的长度
{
cout << charlist[dex] << ' ';
for(int j = 0; hclist[dex][j] != '\0'; j++)
pos += 1;
break;
}
}
}
return 0;
}
三、运行测试:
下标 字符 weight parent lchild rchild
1 A 0.0856 44 0 0
2 B 0.0139 32 0 0
3 C 0.0297 37 0 0
4 D 0.0378 38 0 0
5 E 0.1304 47 0 0
6 F 0.0289 36 0 0
7 G 0.0199 33 0 0
8 H 0.0528 40 0 0
9 I 0.0627 41 0 0
10 J 0.0013 28 0 0
11 K 0.0042 30 0 0
12 L 0.0339 37 0 0
13 M 0.0249 35 0 0
14 N 0.0707 43 0 0
15 O 0.0797 44 0 0
16 P 0.0199 34 0 0
17 Q 0.0012 27 0 0
18 R 0.0677 42 0 0
19 S 0.0607 41 0 0
20 T 0.1045 45 0 0
21 U 0.0249 35 0 0
22 V 0.0092 31 0 0
23 W 0.0149 32 0 0
24 X 0.0017 28 0 0
25 Y 0.0199 34 0 0
26 Z 0.0008 27 0 0
27 0 0.002 29 26 17
28 0 0.003 29 10 24
29 0 0.005 30 27 28
30 0 0.0092 31 11 29
31 0 0.0184 33 22 30
32 0 0.0288 36 2 23
33 0 0.0383 38 31 7
34 0 0.0398 39 16 25
35 0 0.0498 39 13 21
36 0 0.0577 40 32 6
37 0 0.0636 42 3 12
38 0 0.0761 43 4 33
39 0 0.0896 45 34 35
40 0 0.1105 46 8 36
41 0 0.1234 46 19 9
42 0 0.1313 47 37 18
43 0 0.1468 48 14 38
44 0 0.1653 48 15 1
45 0 0.1941 49 39 20
46 0 0.2339 49 40 41
47 0 0.2617 50 5 42
48 0 0.3121 50 43 44
49 0 0.428 51 45 46
50 0 0.5738 51 47 48
51 0 1.0018 0 49 50
A 1 1 1 1
B 0 1 0 1 0 0
C 1 0 1 0 0
D 1 1 0 1 0
E 1 0 0
F 0 1 0 1 1
G 1 1 0 1 1 1
H 0 1 0 0
I 0 1 1 1
J 1 1 0 1 1 0 1 1 1 0
K 1 1 0 1 1 0 1 0
L 1 0 1 0 1
M 0 0 0 1 0
N 1 1 0 0
O 1 1 1 0
P 0 0 0 0 0
Q 1 1 0 1 1 0 1 1 0 1
R 1 0 1 1
S 0 1 1 0
T 0 0 1
U 0 0 0 1 1
V 1 1 0 1 1 0 0
W 0 1 0 1 0 1
X 1 1 0 1 1 0 1 1 1 1
Y 0 0 0 0 1
Z 1 1 0 1 1 0 1 1 0 0
请输入一串由‘A’~‘Z’构成的字符:BUY
该串字符的哈夫曼编码为:0 1 0 1 0 0 0 0 0 1 1 0 0 0 0 1
请输入一串由‘0’和‘1’构成的编码:1110110001101011
该串哈夫曼编码所对应的字符为:O N S R
Process returned 0 (0x0) execution time : 15.130 s
Press any key to continue.