Huffman编码(一)数组实现

哈夫曼编码简介:
一定能获得最优解的算法。
应用领域:压缩
知识扫盲
1.节点的权(W):赋予叶子节点有意义的值
2.节点的路径长度(L):从根几点到当前节点的边的个数。
3.节点的带权路径长度:W*L
4.一棵二叉树的带权路径长度:所有叶子节点的带权路径长度之和
哈夫曼树(最优二叉树)
一棵二叉树的带权路径长度之和最小。
需求:设有一篇英文文章,出现的字母和对该字母出现的频率如下:
a——–9
b——–3
c——–6
d——–4
将字母存放到二叉树的叶子节点中,使得该二叉树的带权路径之和最小。
算法原理
1.将字母和字母所对应的权值看成一个只有根节点的二叉树。
2.将这些二叉树放到一个数组中形成“森林”。
3.每次在森林中寻找权值最小和权值次小的二叉树,将这两个二叉树看成叶子节点(约定:权值最小的为左子树,权值次小的为右字数),重新创建一个二叉树,该二叉树的双亲的权值为这两个二叉树的权值之和。
4.将创建好的二叉树重新放回“森林”中。
5.重复步骤3动作,直到“森林”中只剩下一棵二叉树。
最优二叉树创建完毕!
图示:
最初的森林:
这里写图片描述
第一次创建二叉树:
这里写图片描述
第二次创建二叉树:
这里写图片描述
第三次创建二叉树:
这里写图片描述
此时,“森林中”只有一个二叉树,该哈夫曼树创建完毕。
a—–>0
b—–>110
c——>10
d——>111
[注意]并不是所有的哈夫曼树都是像例子这样向一个方向偏,这只是一种巧合。
数组实现:
1.存储:定义一个结构体数组,
每个元素里面包括:
字母————–char word
权值————–int weight
左子树————int left
右子树————int right
双亲————–int parent
编码————–int *code
默认初始化:
left=right=parent=-1,code=null
数组的长度问题:
该数组中不仅存放叶子节点,还应该存放双亲节点,双亲节点的个数为叶子节点的个数减一
所以设叶子节点为n,则双亲节点为n-1,所以该数组的长度为n+n-1=2*n-1个。
将数组中的元素初始化后如图:
这里写图片描述
经过一次处理后:
这里写图片描述
经过一次处理后:
这里写图片描述
经过一次处理后:
这里写图片描述
数组中只剩下一个没有双亲的二叉树,哈夫曼树创建完毕。
具体代码实现:

//定义存放信息的结构体
typedef struct {
    char word;//用来存储数据
    int weight;//用来存储权值
    int left, right;//用来存储左右子女
    int parent;//用来存储双亲节点
    int *code;//用来存储编码
}HuffNode;
//创建哈夫曼树
HuffNode* creatHuffTree(char str[],int weight[]){
    int i, k1, k2,n;
    n = strlen(str);//记录传入数据的个数
    HuffNode *F = (HuffNode*)malloc(sizeof(HuffNode)*(2*n-1));

    //初始化二叉树中的叶子节点
    for (int i = 0; i < n; i++){
        F[i].code = NULL;
        F[i].parent=F[i].left = F[i].right = -1;
        F[i].word = str[i];
        F[i].weight = weight[i];
    }

    //每次将森林中的二叉树的权值最小和次小的二叉树作为叶子节点,
    //重新构成一棵二叉树
    for (int loop = 0; loop < n - 1; loop++){

        //循环找到森林中第一棵没有双亲的二叉树
        for (k1 = 0; k1 < n  + loop && (F[k1].parent !=-1); k1++);
        //循环找到二叉树中的第二棵没有双亲节点的二叉树
        for (k2 = k1 + 1; k2 < n  + loop && (F[k2].parent !=-1); k2++);

        //找到二叉树中的最小和次小值
        for (i = k2; i < n  + loop; i++){
            if (F[i].parent == -1){//当前二叉树没有双亲
                if (F[i].weight < F[k1].weight){
                    k2 = k1;
                    k1 = i;
                }
                else if(F[i].weight<F[k2].weight){
                    k2 = i;
                }
            }
        }

        //将权值最小二叉树和次小二叉树作为叶子节点,重新创建成一棵二叉树
        //此时出循环的i所指向的数组空间正好为空,所以将双亲节点放到这个位置
        F[i].word = '0';//为了比避免冲突,规定双亲节点word值为0
        F[i].left = k1;//规定权值最小的二叉树为双亲的左子女
        F[i].right = k2;//规定权值次小的二叉树为双亲的有子女
        F[i].parent = -1;
        F[k1].parent = F[k2].parent = i;
        F[i].code = NULL;
        F[i].weight = F[k1].weight + F[k2].weight;
    }
    return F;
}

哈夫曼编码
实现原理:从第一个叶子节点开始,找到双亲节点所在数组的下标,根据双亲节点中的left和right域判断是左子女还是右子女,左子女在code所指向的数组中加入0,右子女加入1,然后该双亲节点作为子女,重复上面的动作,直到找到根节点为止。然后开始编码第二个叶子节点,以此类推,直到编码完毕。
代码实现过程:

//huffman编码
void creatHuffmanCode(HuffNode *F,int n){
    for (int i = 0; i < n; i++){
        int *p = F[i].code = (int*)malloc(sizeof(int)*n);//用来存储huffman编码的数组
        //第一个元素用来记录huffman编码的长度
        p[0] = 0;
        int c= i,pa;
        while (F[c].parent != -1){
            pa = F[c].parent;
            if (F[pa].left == c){
                p[++p[0]] = 0;//规定左子女为0
            }
            else{
                p[++p[0]] = 1;//规定右子女为1
            }
            c = pa;
        }
    }
}
//输出huffman编码
void printHuffmanCode(HuffNode F[], int n){
    for (int i = 0; i < n; i++){
        printf("%c:",F[i].word);
        for (int j = F[i].code[0]; j >0; j--){
            printf("%d",F[i].code[j]);
        }
        printf("\n");
    }
}

【注意】存到code数组中的编码是反的,输出时倒着输出即可。
主函数:

int main(void){
    int weight[8] = { 9, 3,6,4};
    char word[9] = "abcd";
    HuffNode*F=creatHuffTree(word, weight);
    int len = strlen(word);
    creatHuffmanCode(F,len);
    printHuffmanCode(F, len);
    system("pause");
    return 0;
}

测试结果:
这里写图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Huffman编码是一种用于数据压缩的算法,它基于字符的出现频率来构建一种最佳的编码方案。下面我们以一个例子来说明Huffman编码的计算机组成原理。 假设我们有一个字符串“ABBCCCDDDDD”,其中包含了不同的字符A、B、C和D。我们要对这个字符串进行压缩处理。 首先,我们需要统计每个字符在该字符串中出现的次数。通过统计,我们获得如下的频率表: A: 1 B: 2 C: 3 D: 5 接下来,我们要根据频率表来构建Huffman树。Huffman树是一种特殊的二叉树,它的每个叶节点都代表一个字符,而每个非叶节点都代表着两个子节点的字符频率之和。 我们需要按照频率从小到大的顺序将字符构建成最小堆。然后,我们将两个频率最小的字符从堆中取出,构建一个新的节点,频率为这两个字符频率之和,并将这个节点重新放入堆中。以此类推,直到堆中只剩下一个节点,即为Huffman树的根节点。 在我们的例子中,首先将频率为1的字符A和频率为2的字符B构建成两个节点,然后重新放入堆中。接着,频率为3的字符C和频率为3的字符B构建成一个节点,频率为6的字符节点重新放入堆中。最后,将频率为5的字符D和频率为6的字符节点构建成一个新节点,频率为11的节点再次放入堆中。最终,堆中只剩下一个节点,即为Huffman树的根节点。 接下来,我们需要通过遍历Huffman树来得到每个字符的Huffman编码。从根节点开始遍历,对于左子树将编码设置为0,对于右子树将编码设置为1,一直遍历到叶节点,最终得到每个字符的Huffman编码。在我们的例子中,得到的Huffman编码为: A: 010 B: 11 C: 00 D: 10 最后,我们将原始字符串中的每个字符替换为对应的Huffman编码,得到的压缩后的字符串为:01011 00 00 00 10 10 10 10 10。 通过Huffman编码,我们将原始字符串进行了有效的压缩,减少了存储空间的占用。这就是计算机组成原理中Huffman编码的例题的解答过程。 ### 回答2: Huffman编码是一种常用于数据压缩的算法,主要用于将文本中的字符编码为可变长度的比特序列。下面我们以一个简单的例题来说明Huffman编码的原理。 假设我们要对以下文本进行Huffman编码:ABRACADABRA。 首先,我们需要统计文本中每个字符出现的频率。 A:5次 B:2次 R:2次 C:1次 D:1次 接下来,我们创建一个叶子节点数组,每个叶子节点包含一个字符和其对应的频率。对于上述文本,我们可以创建以下叶子节点数组: A: 5 B: 2 R: 2 C: 1 D: 1 然后,我们需要将叶子节点数组转换为一个Huffman树。Huffman树是一种二叉树,其中每个叶子节点表示一个字符,每个非叶子节点表示一个频率。 首先,我们从叶子节点数组中选择两个频率最小的节点,将它们合并为一个新的节点。该新节点的频率为这两个节点的频率之和,并且将这两个节点作为新节点的子节点。 我们重复上述步骤,每次选择频率最小的两个节点合并,直到只剩下一个节点,该节点就是Huffman树的根节点。 在本例中,我们可以生成以下Huffman树: ``` /\ A /\ B /\ /\ C D ``` 接下来,我们从根节点开始遍历Huffman树,并给较低的子节点赋值为0,较高的子节点赋值为1。将叶子节点的路径编码保存起来,即可得到Huffman编码。 在本例中,我们可以得到以下Huffman编码: A: 0 B: 10 R: 11 C: 100 D: 101 最后,我们可以将原始文本ABRACADABRA以Huffman编码的形式进行压缩,即用Huffman编码替换原始字符。压缩后的结果为: 010111011000001011011010010。 这就是使用Huffman编码进行数据压缩的原理和步骤,通过对字符进行频率统计,构建Huffman树,生成Huffman编码,我们可以实现对数据的高效压缩和解压缩。 ### 回答3: Huffman编码是一种用于数据压缩的算法,通过对数据进行特殊编码,可以减少数据存储空间和传输带宽的占用。下面以一个简单的例题来说明Huffman编码的计算过程。 假设有一个文本:“ABBCCCDDDDEEEE”,我们需要对其进行Huffman编码。 1. 统计每个字符在文本中出现的频率: A - 1次 B - 2次 C - 3次 D - 4次 E - 5次 2. 将这些字符及其频率构建成一个权重为频率的小顶堆(或优先队列): A - 1 B - 2 C - 3 D - 4 E - 5 3. 构建Huffman编码树: 从小顶堆中选取两个权重最小的节点,合并为一个节点,权重为两个节点权重之和。将该节点插入小顶堆。重复该过程,直到小顶堆中只剩下一个节点,该节点即为Huffman编码树的根节点。 步骤如下: 1. 合并节点:A - 1,B - 2,合并为节点C - 3。小顶堆更新:C - 3,C - 3,C - 3,D - 4,E - 5。 2. 合并节点:C - 3,C - 3,合并为节点F - 6。小顶堆更新:F - 6,D - 4,E - 5。 3. 合并节点:D - 4,E - 5,合并为节点G - 9。小顶堆更新:F - 6,G - 9。 4. 合并节点:F - 6,G - 9,合并为节点H - 15。小顶堆更新:H - 15。 4. 构建编码: 从Huffman编码树的根节点开始,向左走为0,向右走为1。将每个字符都确定其Huffman编码。 步骤如下: A - 0 B - 10 C - 110 D - 111 E - 11 最终得到的Huffman编码为: A - 0 B - 10 C - 110 D - 111 E - 11 通过Huffman编码,我们可以用更少的二进制位来表示原始数据。在这个例题中,原始数据共有15个字符,每个字符使用8个二进制位来表示,总共需要120个位。而经过Huffman编码后,总共只需要31个位,大大减少了存储和传输的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值