22计算机408考研—数据结构—Huffman的基本结构和操作

872 阅读4分钟

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

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

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

Huffman的基本结构和操作

先看下Huffman树的结构图: 在这里插入图片描述

Huffman树相对于二叉树每个结点多一个`字符`和一个`权重`
权重越大的,越靠近子结点
右面是Huffman树的编码,其实就是结点相对Huffman树根结点的路径(0是选择左结点,1是选择右结点)

Huffman树的特点

没有度为1的结点(每个结点没有子结点,或者有两个子结点,不存在有一个子结点的)
n个叶子结点的哈夫曼树总共有2n-1个结点(Huffman树只有叶子结点才是真的结点,其他结点都是合成结点)
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);

 //huffman编码生成         
void huffmanCodeInit(Tree &tree);

//打印huffman编码
void huffmanCodePrint(Tree tree);

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

//格式化打印huffman树     
void huffmanTreeTypePrint(Tree tree);

Huffman树的初始化

每次都把最小的两个结点合成一个结点,新结点的权值为两个子结点的权值的和,然后把新结点放回原来的地方

我们可以利用层序遍历的方法,把每个结点都放入队列,每次合成取权值最低的两个结点合成,把合成的新结点重新放回队列。
(取最小的两个结点,可以用优先队列,或者每次操作给队列排序,找权值最小的两个结点)

在这里插入图片描述

huffman树初始化(代码)

 //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编码初始化

Huffman的编码每个结点是相对于根结点的路径(0代表左结点,1代表右结点)

比如说:A:0110   根结点 ->左结点权重:42 -> 右结点权重:19 -> 右结点权重:8 -> 左结点权重:5,结点A

在这里插入图片描述

层序遍历(0代表左结点,1代表右结点) 每层的左子结点为:父结点+0 每层的右子结点为:父结点+1

huffman编码初始化(代码)

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