Huffman树创建

本文介绍了如何使用C++实现Huffman树的创建过程,包括节点结构、权重计算、节点合并和Huffman编码的生成。通过示例展示了如何为26个字母和一个空格创建Huffman编码表,并提供了编码和解码函数的实现。
摘要由CSDN通过智能技术生成
#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值