数据结构--哈夫曼树

哈夫曼算法

构造哈夫曼树的方法

  • 1.根据n个给定权值{w1, w2, ..., wn}构成n棵二叉树的森林F = {T1, T2, ..., Tn}, 其中Ti只有一个带权为wi的根结点。

  • 2.在F中选取两棵根结点的权值最小的树作为左右子树,构造一棵新的二叉树,且设置新的二叉树的根结点的权值为其左右子树上的根结点的权值之和。

  • 3.在F中删除这两棵树,同时将新得到的二叉树加入森林中。

  • 4.重复2.和3.直到森林中只有一棵树为止,这棵树就是哈夫曼树

性质

  • 哈夫曼树的节点的度数为0 或 2,没有度为1的节点
  • 包含n个叶子节点的哈夫曼树中共有2n-1个节点
  • 包含n棵树的森林要经过n-1次合并才能形成n-1个新节点

哈夫曼树的存储结构

  • 顺序存储结构

    使用一维结构数组存储,数组元素定义如下:

typedef struct{
  int weight; //节点的权重
  int parent, lch, rch; //父节点,左右孩子节点的下标
}HTNode, *HuffmanTree
    删除节点的方式:更新节点的父节点,及其父节点的左右孩子节点。遍历节点时,要从父节点值不为零的节点中选取。当数组中只有一个节点的父节点值为0时结束。

哈夫曼编码

要设计长度不等的编码,则必须使任意字符的编码都不是另一字符的编码的前缀——前缀编码

  • 统计字符集中每个字符的平均出现频率。
  • 以概率值作为权值构造哈夫曼树,概率越大的节点,路径越短。
  • 在哈夫曼树的分枝上标上0 或 1:节点的左分支标0,右分支标1,把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码。

1. 为什么哈夫曼编码能够保证是前缀编码?

因为没有一片树叶是另一片树叶的祖先,所以每个叶节点的编码就不可能是其他叶节点编码的前缀。

2. 为什么哈夫曼编码能够保证字符编码总长度最短?

因为哈夫曼树的带权路径长度最短,故字符编码的总长度最短。

  • 哈夫曼编码是前缀码且是最优前缀码

3.解码的方法

  • 构造哈夫曼树
  • 依次读入二进制码
  • 读入0,则走向左孩子;读入1,则走向右孩子
  • 一旦到达某叶子节点时,可译出字符
  • 然后再从根出发继续译码,直到结束

C语言实现

构造哈夫曼树

void CreateHuffmanTree(HuffmanTree* HT, int n, int* weights){
    if(n <= 1)return;
    *HT = (HuffmanTree)malloc(sizeof(struct HTNode)*(2*n-1));
    HuffmanTree P = *HT;
    for(int i = 0; i < 2*n-1; i++){
        (P+i)->lch = -1;
        (P+i)->rch = -1;
        (P+i)->parent = -1;
    }
    for(int i = 0; i < n; i++){
        (P+i)->weight = weights[i];
    }
    int min_idx = 0, nmin_idx = 0;
    for(int i = n; i < 2*n-1; i++){
        Select(HT, &min_idx, &nmin_idx, i);
        (P+i)->lch = min_idx;
        (P+i)->rch = nmin_idx;
        (P+i)->weight = (P+min_idx)->weight + (P+nmin_idx)->weight;
        (P+min_idx)->parent = i;
        (P+nmin_idx)->parent = i;
    }
}

构建哈夫曼编码

char** CreateHuffmanCode(HuffmanTree* HT, int n){
    char** HC = malloc(sizeof(char*)*(n));
    char* cd = (char*)malloc(sizeof(char)*n);
    cd[n-1] = '\0';
    HuffmanTree P = *HT;
    for(int i = 0; i < n; i++){
        int start = n-1, c = i, f = (P+i)->parent;
        while(f != -1){
            start--;
            if((P+f)->lch == c)cd[start] = '0';
            else cd[start] = '1';
            c = f;
            f = (P+f)->parent;
        }
        HC[i] = (char*)malloc(sizeof(char)*(n-start));
        strcpy(HC[i], &cd[start]);
    }
    free(cd);
    return HC;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、 一些基本概念1、 路径长度树中一个结点到另一个结点之间的分支构成这两个结点之间的路径,路径上的分支数目为...
    Qi0907阅读 2,608评论 0 1
  • 哈夫曼树 1.1基本介绍 给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称...
    smallmartial阅读 1,734评论 0 0
  • 这篇文章收录在我的 Github 上 algorithms-tutorial,另外记录了些算法题解,感兴趣的可以看...
    Lindz阅读 8,880评论 1 6
  • 哈夫曼编码(Huffman Coding) ◼ 哈夫曼编码,又称为霍夫曼编码,它是现代压缩算法的基础◼ 假设要把字...
    鼬殿阅读 331评论 0 2
  • 好,前面我们介绍了一般二叉树、完全二叉树、满二叉树,这篇文章呢,我们要介绍的是哈夫曼树。哈夫曼树也叫最优二叉树,与...
    绿萝呀阅读 1,399评论 0 1