哈夫曼树存储类型及构造过程

哈夫曼树通过两个权值小的树合并而来,因此仅有度为0的叶子结点和度为2的分支结点。

哈夫曼树:叶子结点 – n个; 度为2的分支结点 – n-1 个;总结点数:2n – 1个

顺序存储

存储类型表示

已知 n个叶子结点,则需要 2n 的数组(0号位不存)去存哈夫曼树

用一维数组结构存储,如图:

结点数据成员 weight + parent + lch + rch

// 结点
typedef int WeightType;  // float、double、..

typedef struct
{
    /* data */
    WeightType weight;
    
    int parent;
    
    int lchild;

    int rchild;

    // 可能存其他的数据

    // char c;
}HNode;
// 一维结构数组
typedef struct
{
    /* data */
    HNode *Nodes;    // 动态分配

    int n;          // 记录叶子结点树,分配 Htree.Nodes = new HNode[2 * n]

    //int r;         // 记录根结点下标
}Htree;

构造过程

初始化 + 构造哈夫曼树,如图:

// 初始化
void InitHuffmanTree(Htree &T, int weight[], int n){
    T.n = n;
    T.Nodes = new HNode[2*n];
    
    for(int i = 1; i < 2*n; i++){
        T.Nodes[i].weight = 0;
        T.Nodes[i].lchild = 0;
        T.Nodes[i].rchild = 0;
        T.Nodes[i].parent = 0;
    }

    for(int i = 1; i <= n; i++)
        T.Nodes[i].weight = weight[i-1]; 
        // 或者直接输入权值(不传weight数组)
        // cin >> T.Nodes[i].weight;
        // 若结点内有数据成员 c,则需要传参 char c[]
        // T.Nodes[i].c = c[i-1];
}

// 找到最小的两个权值的下标(注意:从 parent == 0中找 - 未组成新树)
// 查找可自行完成 - 阿Q用了最笨的办法
void FindSmall(Htree T, int rlimit, int &fir_small, int &sec_small){
    fir_small = sec_small = -1;
    
    // 先找一遍最小值
    for(int i = 1; i < rlimit; i++){
        // 若已组成新树
        if(T.Nodes[i].parent) continue;

        if(fir_small < 0) fir_small = i;
        else if(T.Nodes[i].weight < T.Nodes[fir_small].weight) fir_small = i;
    }

    // 再找一遍倒数第二大的
    for(int i = 1; i < rlimit; i++){
        if(T.Nodes[i].parent || i == fir_small) continue;

        if(sec_small < 0) sec_small = i;
        else if(T.Nodes[i].weight < T.Nodes[sec_small].weight) sec_small = i;
    }
}

// 构造过程
void CreateHuffmanTree(Htree &T){
    int fir_small, sec_small;
    for(int i = T.n+1; i < 2*T.n; i++){
        // 找到最小的两个权值的下标
        FindSmall(T, i, fir_small, sec_small);
        
        // 组新树
        T.Nodes[fir_small].parent = i;
        T.Nodes[sec_small].parent = i;
        T.Nodes[i].lchild = fir_small;
        T.Nodes[i].rchild = sec_small;
        T.Nodes[i].weight = T.Nodes[fir_small].weight + T.Nodes[sec_small].weight;
    }
}
测试代码
#include<iostream>
#include"HuffmanTree.h"

#define n 4

int main(){
    int weight[n] = {7, 5, 2, 4};
    Htree T;
    InitHuffmanTree(T, weight, n);
    CreateHuffmanTree(T);

    int wpl = 0;

    // 打印数组
    std::cout << "index\tWeight\tParent\tlchild\trchild\t" << std::endl;
    for(int i = 1; i < 2*n; i++){
        std::cout << i << "\t" << T.Nodes[i].weight << "\t" << T.Nodes[i].parent << "\t" << T.Nodes[i].lchild << "\t" << T.Nodes[i].rchild << std::endl;
        wpl += T.Nodes[i].weight;
    }

    wpl -= T.Nodes[2*n-1].weight;
    // 打印哈夫曼树的带权路径长度
    std::cout << "\tWPL = " << wpl;

    return 0;
}
/*
输出结果:
index   Weight  Parent  lchild  rchild
1       7       7       0       0
2       5       6       0       0
3       2       5       0       0
4       4       5       0       0
5       6       6       3       4
6       11      7       2       5
7       18      0       1       6
        WPL = 35
*/

链式存储

哈夫曼树是二叉树,因此当然可以使用二叉链表存储咯

构造过程

阿Q用优先队列去快速解决构造问题 (哈夫曼树概念及构造算法题给阿Q的启示)

#include<queue>
#include<vector>

typedef struct HNode
{
    /* data */
    // char c;

    int weight;

    struct HNode *lchild, *rchild;
}HNode, *Htree;

// 定义自定义比较器
struct CompareHNode {
    bool operator()(const HNode* a, const HNode* b) const {
        return a->weight > b->weight; // 小顶堆(升序排列,队首最小); 比较器的返回值直接控制元素的排列顺序,但它的语义是“谁应该排在后面”
        // 若需要大顶堆,改为 a->weight < b->weight
    }
};

typedef std::priority_queue<HNode*, std::vector<HNode*>, CompareHNode> Prior_Queue;

// 初始化
void InitHuffmanTree(Prior_Queue &q, int weight[], int n){
    // 构造森林全是树
    for(int i = 0; i < n; i++){
        HNode *node = new HNode;
        node->weight = weight[i];
        node->lchild = nullptr;
        node->rchild = nullptr;
        // 结点若有数据成员 c,则需要传参 char c[]
        // node->c = c[i];  
        q.push(node);
    }
}

// 构造过程
Htree CreateHuffmanTree(Prior_Queue &q){
    HNode *fir_small, *sec_small;

    while(q.size() > 1){
        // 选用两小造新树,删除两小添新树
        fir_small = q.top();
        q.pop();
        sec_small = q.top();
        q.pop();

        HNode *newNode = new HNode;
        newNode->weight = fir_small->weight + sec_small->weight;
        newNode->lchild = fir_small;
        newNode->rchild = sec_small;

        q.push(newNode);
    }

    // 返回根结点
    Htree Root = q.top();

    return Root;
}

小结

哈夫曼树是特殊二叉树,是带权路径长度最短的二叉树。

主要关注于 如何创建出哈夫曼树即可。

暂无评论

发送评论 编辑评论


				
上一篇
下一篇