九、赫夫曼树及其应用

1. 最优二叉树(赫夫曼树)

带权路径长度(WPL):设二叉树有n个叶子结点,每个叶子结点带有权值WK,从根结点到每个叶子结点的长度为Lk,则每个叶子结点的带权路径长度之和就是: WPL = ∑WkLk
最优二叉树(赫夫曼树): WPL最小的二叉树,每次把权值最小的两棵二叉树合并。
赫夫曼树的特点:

  • 没有度为1的结点;
  • n个叶子结点的赫夫曼树共有2n-1个节点;
  • 赫夫曼树的任意非叶子结点的左右子树交换后仍为赫夫曼树。
  • 对同一组权值{w1,w2,......,wn}, 是否存在不同构的两棵赫夫曼树呢?
    对一组权值{1,2,3,3}, 不同构的两棵哈夫曼树:

构建赫夫曼树

typedef struct TreeNode *HuffmanTree;
struct TreeNode{
    int weight;
    HuffmanTree left,right;
};
构建赫夫曼树
HuffmanTree Huffman(MinHeap H)
{
    int i;
    HuffmanTree T;
    buildMinHeap(H);
    for (i = 1; i < H->size; i++) {
        T = malloc(sizeof(struct TreeNode));
        T->left = deleteMin(H);
        T->right = deleteMin(H);
        T->weight = T->left->weight + T->right->weight;
        insert(H, T);
    }
    T = deleteMin(H);
    return T;
}
  • 构建赫夫曼树的整体时间复杂度为 T = O(N logN)

2. 赫夫曼编码

为了让编码尽可能的短,我们进行不等长编码。如下:
a: 1 e: 0 s: 10 t: 11
1011是什么字符串的编码?
aeaa: 1011
aet: 1011
st: 1011
因此这么编码会产生二义性
如何避免二义性呢?
前缀编码:任何字符的编码都不是另一个字符编码的前缀。

我们用二叉树进行编码:
(1) 左右分支:0、1
(2) 字符只在叶子结点上

四个字符的频率:a:4, u:1, x:2, z:1

  1. 用等长编码方式:


  2. 用赫夫曼树进行编码方式:


  • 可以看出编码长度乘以频率之和后者较小。而且没有二义性。

赫夫曼编码代码:

typedef struct TreeNode *HuffmanTree;
typedef struct TreeNode HTNode;
struct TreeNode{
    unsigned int weight;
    unsigned int left,right, parent;
    bool minFlag;
};
typedef char** HuffmanCode;

void selectMins(HuffmanTree HT,int end, int *s1, int *s2)
{
    HTNode *minp1 = NULL;
    HTNode *minp2 = NULL;
    for (int i = 1; i <= end; i++) {
        HTNode *node = &HT[i];
        if(node->minFlag)
            continue;
        if(minp1 == NULL){
            minp1 = node;
            *s1 = i;
        }else{
            minp2 = node;
            *s2 = i;
            break;
        }
    }
    if(minp1->weight > minp2->weight)
    {
        HTNode* tmp = minp1;
        minp1 = minp2;
        minp2 = tmp;
        
        int tmpI = *s1;
        *s1 = *s2;
        *s2 = tmpI;
    }
    
    for (int i = 1; i <= end; ++i) {
        
        HTNode *node = &HT[i];
        if(node->minFlag)
            continue;
        if(node->weight < minp1->weight){
            minp1 = node;
            *s1 = i;
        }else if (node->weight < minp2->weight && node != minp1){
            minp2 = node;
            *s2 = i;
        }
    }
    minp1->minFlag = true;
    minp2->minFlag = true;
}

void HuffmanCoding(int *w, int n)
{
    if(n <= 1) return;
    int m = 2 * n - 1;
    HuffmanTree HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode));
    HuffmanTree p;
    int i, s1, s2;
    int *wp = w;
    for(p = HT + 1, i = 1; i<=n; ++i, ++p, ++wp)
    {
        p->weight = *wp;
        p->left = 0;
        p->right = 0;
        p->parent = 0;
        p->minFlag = false;
    }
    
    for (; i <= m; ++i, ++p) {
        p->weight = 0;
        p->left = 0;
        p->right = 0;
        p->parent = 0;
        p->minFlag = false;
    }
    
    for (i = n + 1; i <= m; ++i) {
        selectMins(HT, i-1, &s1, &s2);
        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;
        HT[i].minFlag = false;
    }
    
    for(int j = 1; j <= m; j++)
    {
        printf("w:%u, left:%u, right:%u, parent:%u\n", HT[j].weight,HT[j].left,HT[j].right,HT[j].parent);
    }
    HuffmanCode HC = (HuffmanCode)malloc((n+1)*sizeof(char*));
    char *cd = (char *)malloc(n * sizeof(char));
    cd[n-1] = '\0';
    int start , c, f;
    for (int i = 1; i <= n; ++i) {
        start = n - 1;
        for (c = i, f = HT[i].parent; f!=0; c = f, f = HT[f].parent)
        {
            if(HT[f].left == c) cd[--start] = '0';
            else cd[--start] = '1';
        }
        
        HC[i] = (char *)malloc((n - start) *sizeof(char));
        strcpy(HC[i], &cd[start]);
        printf("w:%u %s \n",w[i-1],HC[i]);
        free(HC[i]);
    }
    free(cd);
    free(HC);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容