#include <bits/stdc++.h>
/****
创建 huffman 树
假定有 n 个叶子节点,共需要 2n-1 个节点
节点的存储结构在结构体中定义
这些节点使用结构体数组来存,下标即是节点的位置
//64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1 168
**/
using namespace std;
struct HTNode {
int weight; //节点的权重
int parent, left, right; //亲 左右孩子的下标
};
typedef HTNode* HuffmanTree;
typedef vector<string> HuffmanCode;//动态分配数组存储HuffmanCode
//26 letters and a blank
//前 26 个存的是字母的,最后一个是空格
const int n = 27;
vector<int> weights (27, 0);
HuffmanTree HT;
HuffmanCode HC;
//这个实验是27个输入,但是为了通用,这里设置为n.同理,上面的weights数组也需要改变一下。
//这里避免麻烦,不做修改。
void getWeight(int n) {
for(int i=0; i<n; ++i)
cin >> weights[i];
}
//在HuffmanTree表中前n行中找最小的两个,赋值给s1, s2
void Select(HuffmanTree& HT, int n, int& s1, int& s2) {
//两次遍历可以考虑使用一个双重循环
bool first = true;
for(int j=1; j<=n; ++j) {
if(HT[j].parent == 0) {
if(first) {
s1 = j;
first = false;
}
else{
if(HT[j].weight < HT[s1].weight) s1 = j;
}
}
}
HT[s1].parent = 1;//随便标记一下 不然后面又要被选到
first = true;
for(int j=1; j<=n; ++j) {
if(HT[j].parent == 0) {
if(first) {
s2 = j;
first = false;
}
else{
if(HT[j].weight < HT[s2].weight) s2 = j;
}
}
}
return;
}
/**
n 表示有叶子结点的个数
HT HTNode* 类型的指针,后面需要初始化为长度为2n的数组的指针 因为0号没有用
*/
void CreateHuffmanTree(HuffmanTree& HT, int n) {
//前半部分是初始化
if(n<=1) return;
cout << "calling CreateHuffmanTree" << endl;
int m = 2*n - 1;
HT = new HTNode[m+1];//0号元素未用
//初态 都初始化为0
for(int i=1; i<=m; ++i) {
HT[i].parent = 0;
HT[i].left = 0;
HT[i].right = 0;
}
//给前n个单元的节点输入权重
//注意 有的地方0下标的没有用,有的用了 这里要记清楚
//或者说,是否应该统一规定一下,都要用或者都不用
for(int i=1; i<=n; ++i)
HT[i].weight = weights[i-1];
//初始化结束 下面开始创建HuffmanTree
for(int i=n+1; i<=m; ++i) {
//在HT前i-1个节点中选择亲为0且权重最小的两个节点,
//返回他们的编号s1, s2.严格来说,s1比s2更小
int s1, s2;
Select(HT, i-1, s1, s2);
// cout << s1 << " " << s2 << endl;
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].left = s1;
HT[i].right = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
return;
}
//创建 HuffmanCode 就是从前面的HT表格中搜索
void CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n) {
/** 将HuffmanCode用vector<string> 来存,实现更简单。因为 c语言中char比较麻烦。不过效率应该比较高 **/
int current;
int parent;
for(int i=1;i<=n;++i){
string code = "";
current = i;
while(HT[current].parent != 0){
parent = HT[current].parent;
if(HT[parent].left == current) code+= "0";
else code += "1";
current = parent;
}
reverse(code.begin(), code.end()); //head file :algorithm
// if(i <= 26) cout << char('a'+i-1) << ":" << code << endl;
// else cout << " :" << code << endl;
HC.push_back(code);
}
return;
/**下面是按照char*数组来写的*/
// //从叶子到根逆序求每个字符的HuffmanCode, 存储在HC中
// HC = new char*[n+1];
// char* cd = new char[n];
// cd[n-1] = '\0';
//
// for(int i=1; i<=n; ++i) {
// int start = n-1;//因为本来就是向上找,是逆序的,所以从后向前
// int c=i; //c 是当前节点, f是它的亲节点
// int f = HT[i].parent;
// while(f!=0) {
// start--;
// if(HT[f].lchild == c) cd[start] = '0';
// else cd[start] = '1';
// c = f;
// f = HT[f].parent;
// }
// HC[i] = new char[n-start];
// strcpy(HC[i], cd[start]);
// }
// delete cd;
}
string encode(string message){
string code;
int len = message.length();
for(int i=0;i<len;++i){
//get code for letter[i]
char letter = message[i];
if(isalpha(letter)) code += HC[int(letter - 'a')];
else code += HC[n-1];
}
return code;
}
/**
Wondering where is the bug......
这个decode2有问题,先使用decode
先使用下面重写的decode(string code),这个有时间再改
**/
string decode2(string code){
//需要进行深度优先搜索,找到编码的划分,然后再去编码表中查找。
string message;
int root;//先找到树根
for(int i=1;i<=2*n;++i){
if(HT[i].parent == 0){
root = i;
break;
}
}
cout << "root:" << root << endl;
int left=0;
int right = left;
int len = code.length();
int temp_root = root;
while(left<len){
temp_root = root;
//任意节点左右孩子要么全0,要么全不是0
while(HT[temp_root].left!=0){
// cout << "right:" << right << endl;
// cout << "code[right]:" <<code[right]<<endl;
if(code[right] == 0) temp_root = HT[temp_root].left;
else temp_root = HT[temp_root].right;
cout << temp_root;
right ++;
}
cout <<endl;
right ++;//到达叶节点了,不能往下搜索,但是当前位置要保存
string letter_code = code.substr(left, right-left);
cout << "left:" <<left << endl;
cout <<"right:" <<right << endl;
cout << "letter_code:" << letter_code << endl;
for(int i=0;i<26;++i){
if(letter_code == HC[i]) {
message += char(i+'a');
// cout << "letter_code:" << letter_code << endl;
cout <<"*****************letter:" << char(i+'a') << endl;
break;
}
}
if(letter_code == HC[26]) {
message += " ";
// cout << "letter_code:" << letter_code << endl;
cout << "********************blank: " << endl;
}
left = right;
}
return message;
}
string decode(string code){
string message;
int len = code.length();
int left = 0;
//这里也可以设置一些判断条件,来检查输入的编码不合法的情况
//解码过程中一旦出现不能解析的就要退出,否则可能歪打正着解析出某些信息
while(left < len){
bool invalid = true;
for(int i=0;i<27;++i){
string letter_code = HC[i];
if(len-left >= letter_code.length()){
if(code.substr(left, letter_code.length()) == letter_code){
invalid = false;
if(i < 26) message+=char(i+'a');
else message+=" ";
left+=letter_code.length();
break;
}
}
}
if(invalid){
cout << "输入的编码有误!"<<endl;
return "";
}
}
return message;
}
void printHuffmanTree(){
HTNode temp;
for(int i=1;i<2*n;++i){
temp = HT[i];
cout << setw(5) << i << " " << temp.weight << " " << temp.parent
<< " " << temp.left << " " << temp.right << endl;
}
}
void prompt() {
cout << "1---输入HuffmanTree的参数(26字母和空格)" << endl; //getWeight()
cout << "2---创建HuffmanTree和编码表" << endl; //CreateHuffmanTree
cout << "3---输出编码表" << endl;
cout << "4---输入编码,翻译为字符" << endl;
cout << "5---输入字符,输出编码" << endl;
cout << "其他任意值退出" << endl;
}
/**
释放之前分配的空间
**/
void Destroy(){
delete[] HT;
}
int main() {
// 同上所述,这个实验是27,其他的场景未必是。
// int n = 27;
prompt();
int choice;
while(true){
cout << "请输入选择:";
cin >> choice;
if(choice == 1){
cout << "请输入26个字母(a-z)和一个空格出现的频数:";
getWeight(n);
} else if(choice ==2){
CreateHuffmanTree(HT, n);
CreateHuffmanCode(HT, HC, n);
cout << "Success!" << endl;
// cout << "下面输出表格:" <<endl;
// printHuffmanTree();
}
else if (choice ==3){
for(int i=1;i<n;++i){
cout << char('a' + i - 1) << ":";
cout << HC[i-1] << endl;
}
cout << "空格:";
cout << HC[n-1] << endl;
}
else if(choice == 4){
cout << "请输入编码:";
string code;
cin >> code;
string message = decode(code);
cout << "翻译为字符后:" << message << endl;
}
else if(choice == 5){
cout << "请输入信息:";
string message;
// cin >> message;
string temp;
getline(cin, temp);
getline(cin, message);
cout << "message:"<<message<<endl;
string code = encode(message);
cout << "编码后的编码:" << code << endl;
}
else break;
}
//集中释放空间
Destroy();
return 0;
}
Huffman树创建
最新推荐文章于 2022-07-07 17:22:25 发布
本文介绍了如何使用C++实现Huffman树的创建过程,包括节点结构、权重计算、节点合并和Huffman编码的生成。通过示例展示了如何为26个字母和一个空格创建Huffman编码表,并提供了编码和解码函数的实现。
摘要由CSDN通过智能技术生成