22计算机408考研—数据结构—Huffman树+编码

170 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

手把手教学考研大纲范围内树定义,遍历,Huffman,并查集 22考研大纲数据结构要求的是C/C++,笔者以前使用的都是Java,对于C++还很欠缺, 如有什么建议或者不足欢迎大佬评论区或者私信指出 初心是用最简单的语言描述数据结构

Talk is cheap. Show me the code. 理论到处都有,代码加例题自己练习才能真的学会

Huffman树+编码

#include "iostream"
#include "queue"
#include "unordered_map"
#include "math.h"
#include "string"
#include "string.h"

using namespace std;
typedef struct TreeNode {   //定义Huffman树结构体:字符,权重,huffman编码,左子结点,右子结点
    char c;
    int weight;
    char huffman_code[100];
    struct TreeNode *lefttree;
    struct TreeNode *righttree;
}TreeNode, *Tree;

struct cmp{ //定义树结点排序规则,按权重从小到大排序
    bool operator()(Tree a, Tree b) {
        return a->weight > b->weight;
    }
};

    //huffman树的初始化
void huffmanTreeInit(Tree &tree,unordered_map<char, int> words) {   //用unordered_map保存每个字符出现的次数
                                    //map和unordered_map差不多的,map是顺序的,unordered_map是按照hash顺序的
                        //!!!优先队列,符合排序的顺序,但是调试的时候里面的顺序不是排序的,弹出的时候是按照排序后的顺序弹出的
                        //!!!优先队列,底层为堆排序,是存储找vector或deque里面,所以调试的时候看到的不是排好序的
    priority_queue<Tree, vector<Tree>,cmp> nodes;
    unordered_map<char, int> :: iterator words_it;  //创建一个unordered_map的迭代器
    for (words_it = words.begin(); words_it != words.end(); words_it++) {   //循环map里面的内容
        Tree temp = new TreeNode;     //创建树结点,map保存的字符和数量,赋值给树结点
        temp->c = words_it->first;
        temp->weight = words_it->second;
        temp->lefttree = NULL;       //左右子结点赋空值
        temp->righttree = NULL;
        nodes.push(temp);       //把创建的树结点保存到优先队列里
    }
                    //优先队列保证队列内得到元素都是按照自定义的排序规则排序的
    while (!nodes.empty()) {    //只要优先队列不为空,证明还有结点未访问
        if (nodes.size() == 1) {    //当剩下一个结点的时候,无需在合并,把这个结点给tree,从队列弹出结点
            tree = nodes.top();
            nodes.pop();
        } else {
            Tree temp1 = nodes.top();   //取出权重小的两个结点
            nodes.pop();
            Tree temp2 = nodes.top();
            nodes.pop();

            Tree newtemp = new TreeNode;    //把两个权重小的结点合成一个结点
            newtemp->weight = temp1->weight + temp2->weight;
            if (temp1->weight < temp2->weight) {    //权重小的放到左子结点
                newtemp->lefttree = temp1;
                newtemp->righttree = temp2;
            } else {
                newtemp->lefttree = temp2;
                newtemp->righttree = temp1;
            }
            newtemp->c = '0';   //非叶子结点字符赋 0
            nodes.push(newtemp);    //把新结点入优先队列
        }
    }

}

    //huffman编码生成           0代表左子结点,1代表右子结点
void huffmanCodeInit(Tree &tree) {  //用层序遍历,每一层huffman编码多一位
    Tree temp = tree;
    queue<Tree> nodes;
    nodes.push(temp);
    while (!nodes.empty()) {
        temp = nodes.front();   //访问到当前结点,把当前结点从队列弹出
        nodes.pop();
        if (temp->lefttree != NULL) {   //左子结点不为空,就把左子结点放入优先队列,
            nodes.push(temp->lefttree);
            strcpy(temp->lefttree->huffman_code, temp->huffman_code);   //左结点huffman编码=父结点huffman编码 + 0
            char *str = temp->lefttree->huffman_code;
            while (*str != '\0') {
                *str++;
            }
            *str = '0';
        }
        if (temp->righttree != NULL) {  //与左子结点同理
            nodes.push(temp->righttree);
            strcpy(temp->righttree->huffman_code, temp->huffman_code);
            char *str = temp->righttree->huffman_code;
            while (*str != '\0') {
                *str++;
            }
            *str = '1';
        }
    }
}

    //打印huffman编码
void huffmanCodePrint(Tree tree) {
    priority_queue<Tree, vector<Tree>, cmp> nodes;
    queue<Tree> queue;
    queue.push(tree);
    while (!queue.empty()) {    //先层序遍历,把每个结点都保存到优先队列里面
        Tree temp = queue.front();
        queue.pop();
        if (temp->c != '0') {   //如果字符是 '0' 此结点为非叶子结点,并不是真正的huffman结点
            nodes.push(temp);
        }
        if (temp->lefttree != NULL) {
            queue.push(temp->lefttree);
        }
        if (temp->righttree != NULL) {
            queue.push(temp->righttree);
        }
    }
    while (!nodes.empty()) {    //优先队列(保证队列是有序的)  有序输出每个树结点
        Tree t = nodes.top();
        nodes.pop();
        cout << "字符: " << t->c << " 权值: " << t->weight << " 编码: " << t->huffman_code << endl;
    }
}

    //得到当前huffman树的深度
int getDepth(Tree tree) {

    if (tree == NULL) {
        return 0;
    }
    return max(getDepth(tree->lefttree), getDepth(tree->righttree)) + 1;
}

    //格式化打印huffman树     和链式存储二叉树的格式化输出差不多
void huffmanTreeTypePrint(Tree tree) {
    cout << "打印二叉树:  两行为一层,第一层为字符,第二层为权值 \n";
    cout << "   第一层字符: 字符为 0 说明下面有结点, 字符为 N 说明为空结点 \n";
    cout << "   第一层权值: 权值为 0 \n" << endl;
    int depth = getDepth(tree);
    queue<Tree> q;
    q.push(tree);
    for (int i = 0; i < depth; i++) {
        int space = 0;
        for (int j = 0; j < depth - 1 - i; j++) {
            space = space * 2 + 1;
        }

        for (int j = 0; j < space; j++) {       //输出huffman字符
            cout << " ";
        }
        queue<Tree> temp = q;
        for (int j = 0; j < pow(2, i); j++) {
            cout << q.front()->c;
            if (q.front()->lefttree != NULL) {
                q.push(q.front()->lefttree);
            } else {
                TreeNode *t = new TreeNode;
                t->c = 'N';
                t->lefttree = NULL;
                t->righttree = NULL;
                q.push(t);
            }
            if (q.front()->righttree != NULL) {
                q.push(q.front()->righttree);
            } else {
                TreeNode *t = new TreeNode;
                t->c = 'N';
                t->lefttree = NULL;
                t->righttree = NULL;
                q.push(t);
            }
            q.pop();

            for (int k = 0; k < space * 2 + 1; k++) {
                cout << " ";
            }
        }
        cout << "\n";


        for (int j = 0; j < space; j++) {       //输出huffman权值
            cout << " ";
        }
        for (int j = 0; j < pow(2, i); j++) {
            cout << temp.front()->weight;
            if (temp.front()->lefttree != NULL) {
                temp.push(temp.front()->lefttree);
            } else {
                TreeNode *t = new TreeNode;
                t->weight = 0;
                t->lefttree = NULL;
                t->righttree = NULL;
                temp.push(t);
            }
            if (temp.front()->righttree != NULL) {
                temp.push(temp.front()->righttree);
            } else {
                TreeNode *t = new TreeNode;
                t->weight = 0;
                t->lefttree = NULL;
                t->righttree = NULL;
                temp.push(t);
            }
            temp.pop();

            for (int k = 0; k < space * 2 + 1; k++) {
                cout << " ";
            }
        }
        cout << "\n";
    }
    cout << endl;
}


int main() {
    Tree tree;
    unordered_map<char, int> words; //用来存每个字符以及字符出现的个数
    string s;
    cin >> s;
    unordered_map<char, int> :: iterator words_it;  //定义map迭代器,用来表示map的某个结点
    for (int i = 0; i < s.length(); i++) {
        words_it = words.find(s[i]);    //找到s[i]字符的结点
        if (words_it == words.end()) {  //如果找到map结尾,说明不存在当前字符的结点
            words.insert(make_pair(s[i], 1));   //添加当前字符,当前字符出现的次数为1
        } else {    //如果找到了,就把当前字符出现的次数 + 1
            words_it->second++;
        }
    }
    //构建huffman树
    huffmanTreeInit(tree, words);
    //构建完成 huffman树,构建huffman编码
    huffmanCodeInit(tree);
    //格式化输出huffman树
    huffmanTreeTypePrint(tree);
    //输出huffman编码
    huffmanCodePrint(tree);
    return 0;
}