【数据结构15】哈夫曼树构建、编码(附手绘图解)

目录

一、基本知识

二、Huffman树编码实现

[2.1 Huffman树创建](#2.1 Huffman树创建)

[2.2 Huffman树的编码](#2.2 Huffman树的编码)

[2.3 代码实现Huffman树的创建](#2.3 代码实现Huffman树的创建)

[2.4 代码实现Huffman树的编码](#2.4 代码实现Huffman树的编码)


一、基本知识

路径:一个结点到另一个结点的通路被称为路径;如A结点到F结点之间的通路

路径长度:根到子节点每走一条边,长度 + 1,根节点路径长度为 0;又在一棵树中,规定根结点所在层数为1,那从根结点到第i层结点的路径长度为i-1,F结点的路径长度为3

节点的带权路径长度:(给每一个节点赋予一个新的数值,被称为这个结点的权),根到节点的路径长度 × 节点权重为该节点的带权路径长度;如F结点的带权路径长度为3*5

树的带权路径长度(WPL):为树中所有叶子结点的带权路径长度之和;如上图WPL = 5 * 3 + 2 * 4

等长编码:如ABCD分别用011 010 101 111表示,都是三个bit,是等长编码

变长编码:如在(AABCAADBBCD)A:1表示 B:01表示 C:001表示 次数出现概率高的用较短的编码表示,可以节省空间

前缀码:如A:11 B:110 ,11是110的前缀,这种是不合法前缀码,合法前缀码需任意编码都不是其他编码前缀(如此才能无歧义解码)

哈夫曼树:哈夫曼编码是一种合法的前缀码,同时也是最优变长编码------它不会出现前缀码冲突(不合法),且树的带权路径长度(WPL)最小 (哈夫曼编码一定是合法前缀码)

二、Huffman树编码实现

2.1 Huffman树创建

例:节点A-H、weight[] = {5,29,7,8,14,23,3,11,8}

2.2 Huffman树的编码

2.3 代码实现Huffman树的创建

复制代码
C

static void selectNode(HuffmanTree tree,int n,int *s1,int *s2)
{
    int mini = 0;
    // 找到第一个父节点为0的编号
    for (int i = 1;i <= n;++i)
    {
        if (tree[i].parent == 0)
        {
            mini = i;
            break;
        }
    }
    for (int i = 1;i<=n;++i)
    {
        if (tree[i].parent == 0)
        {
            if (tree[i].weight < tree[mini].weight)
            {
                mini = i;
            }
        }
    }
    *s1 = mini;
    // 开始找第二个最小权值的点
    for (int i=1;i<=n;++i)
    {
        if (tree[i].parent == 0 && i != *s1)
        {
            mini = i;
            break;
        }
    }
    for (int i = 1;i<=n;++i)
    {
        if (tree[i].parent == 0 && i != *s1)
        {
            if (tree[i].weight < tree[mini].weight)
            {
                mini = i;
            }
        }
    }
    *s2 = mini;
}
HuffmanTree createHuffmanTree(const int* w, int n)
{
    int m = 2*n -1;
    // 1.1 申请2n个单元,从1号索引开始存储数据
    HuffmanTree tree = malloc(sizeof(HuffmanNode) * (m+1));
    if (tree == NULL)
    {
        return NULL;
    }
    // 初始化1~2n-1个节点
    for (int i = 1;i<=m;++i)
    {
        tree[i].parent = tree[i].lChild = tree[i].rChild = 0;
        tree[i].weight = 0;
    }
    // 1.2设置初始化权值
    for (int i = 1;i<=n;++i)
    {
        tree[i].weight = w[i-1];
    }
    int s1,s2;
    // 填充n+1下标到m下标的空间
    for (int i = n+1;i<=m;++i)
    {
        // 在[1...i-1]范围内,父节点为0,权值最小的两个点
        selectNode(tree,i-1,&s1,&s2);
        // 将这2个权值最小的节点,组合到第i个位置上(父节点)
        tree[s1].parent = tree[s2].parent = i;
        tree[i].lChild = s1;
        tree[i].rChild = s2;
        tree[i].weight = tree[s1].weight + tree[s2].weight;
    }
    return tree;
}

2.4 代码实现Huffman树的编码

复制代码
C

HuffmanCode* createHuffmanCode(HuffmanTree tree, int n)
{
    // 生成n个字符的编码表,每个表项里保存编码的空间首地址
    HuffmanCode *codes = malloc(sizeof(HuffmanCode) * n);
    if (codes == NULL)
    {
        return NULL;
    }
    memset(codes,0,sizeof(HuffmanCode) * n);
    // 每求一个字符时,倒序构建,n个节点,树的高度最高是n,编码个数最多为n
    char *temp = malloc(sizeof(char) * n);
    int start;
    int p;      // 记录第i个位置的父节点
    int pos;    // 记录当前位置i
    for (int i = 1;i <= n;++i)
    {
        start = n-1;          // temp、start组合使用,从后往前
        temp[start] = '\0';   // n-1位置放'\0'
        pos = i;
        p = tree[i].parent;
        while (p)         // 第i个位置的父节点存在
        {
            --start;
            // 该父节点左孩子是pos(i),则'0',否则是右孩子'1'
            temp[start] = tree[p].lChild == pos ? '0':'1';
            pos = p;
            p = tree[p].parent;
        }
        // huffman树节点1-n,codes数组下标0-n-1
        codes[i-1] = malloc(sizeof(char) * (n-start));
        strcpy(codes[i-1],&temp[start]);  // 将temp中存的编码复制,至codes[i-1]位置
    }
    free(temp);
    return codes;
}

编码过程:

两数组:**codes:用来存编码 、temp:是临时数组,与start变量从数组尾到头编码

两变量: pos:存当前位置 、 p:存当前位置的父节点

每经历一个while循环,就将temp临时数组中存的内容复制到codes中

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

相关推荐
bnmoel10 小时前
数据结构深度剖析二叉树・上篇:基础概念、结构特性、存储结构全解析
c语言·数据结构·二叉树·
Dlrb121111 小时前
数据结构-单链表与双链表
c语言·数据结构·链表·排序·双链表
小龙报11 小时前
【优选算法】双指针专项:1.移动零 2. 复写零 3.快乐数
java·c语言·数据结构·c++·python·算法·面试
sukioe12 小时前
深入理解 MySQL 索引:底层数据结构与 B+ 树设计原理
数据结构·mysql·oracle
TDengine (老段)12 小时前
TDengine MemTable 深度解析 — 内存写入缓冲区的数据结构与生命周期
大数据·数据结构·数据库·物联网·时序数据库·tdengine·涛思数据
-To be number.wan12 小时前
算法日记 | C++ 结构体
数据结构·学习·算法
CS创新实验室12 小时前
数据结构和算法:摊还分析
java·数据结构·算法
curry____30312 小时前
邻接矩阵 和 领接表 和 链式前向星对比
数据结构·c++·算法
he___H12 小时前
leetcode100-合并区间
java·数据结构·算法