数据结构——哈夫曼编码

概念

  • 叶子节点的权值:对叶子节点赋予的一个有意义的数值
  • 二叉树的带权路径长度:从根节点到叶子节点的路径长度 X 相应叶子节点的权值
  • 哈夫曼树:给定一组具有确定权值的叶子节点,可以构造出不同的二叉树,其中带权路径长度最小的二叉树称之为哈夫曼树

设计

  • 将单个节点看成是一棵没有parent、lchild、rchild的三叉树,那么权值分别为{w1…wn}可以形成n棵单节点的三叉树森林。

  • 每次挑选当前树的根节点权值最小的两棵树,组建成一个新树。因为叶子节点为n,哈夫曼树的总节点数为2n-1。所以需要进行n-1 次的合并。

  • 单节点的存储结构为

    parent 、lchild、rchild、weight

    其中parent、lchild、rchild分别为节点的索引。哈夫曼树的结构为一个连续的数组。(缺点:创建的哈夫曼树规模会被限制。)

    typedef struct huffman_node{
        int weight;
        int lchild,rchild,parent;
    }Element;
    

算法设计

# include <iostream>
# include <stdlib.h>
# include <stdio.h>

using namespace std;

typedef struct huffman_node{
    int weight;
    int lchild,rchild,parent;
}Element;

void Select2min(Element * huffnodes,int n,int & m1,int &m2){
    for(int i = 0;i < n;i++){
        if(huffnodes[i].parent == -1){
            m1 = i;
            break;
        }
    }
    for(int i = 0;i < n;i ++){
        if(huffnodes[i].parent == -1 && huffnodes[i].weight < huffnodes[m1].weight){
            m1 = i;
        }        
    }

    for(int i = 0;i < n;i++){
        if(huffnodes[i].parent == -1 && i != m1){
            m2 = i;
            break;
        }
    }
    for(int i = 0;i < n;i ++){
        if(huffnodes[i].parent == -1 && huffnodes[i].weight < huffnodes[m2].weight && i != m1){
            m2 = i;
        }        
    }
    cout << "m1 : " << m1 <<"   m2 : " << m2 << endl;
}

void InitHuffnodes(Element *huffnodes,int n){
    for(int i = 0;i < n;i++){
        huffnodes[i].parent = -1;
        huffnodes[i].lchild = -1;
        huffnodes[i].rchild = -1;
    }
}

void HuffmanTree(Element* huffnodes,int * w,int n){
    InitHuffnodes(huffnodes,2*n-1); 

    for(int i = 0;i < n;i++){
        huffnodes[i].weight = w[i];
    }
    for(int t = n;t < 2 * n -1 ;t++){
        int m1,m2;
        Select2min(huffnodes,t,m1,m2);
        huffnodes[m1].parent = t;
        huffnodes[m2].parent = t;
        huffnodes[t].lchild = m1;
        huffnodes[t].rchild = m2;
        huffnodes[t].weight = huffnodes[m1].weight + huffnodes[m2].weight;
    }
}

void PrintHuffmanTree(Element* huffnodes,int n){
    string index = "index"; 
    string weight = "weight"; 
    string parent = "parent"; 
    string lchild = "lchild"; 
    string rchild = "rchild"; 
    printf("%-8s%-8s%-8s%-8s%-8s\n",index.c_str(),weight.c_str(),parent.c_str(),lchild.c_str(),rchild.c_str());
    for(int i = 0;i < n;i++){
        printf("%-8d%-8d%-8d%-8d%-8d\n",i,huffnodes[i].weight,huffnodes[i].parent,huffnodes[i].lchild,huffnodes[i].rchild);
    }
}

int main(){
    int w[] = {15,9,7,8,4,11,3,31};
    int n = sizeof(w)/sizeof(int);

    Element* huffnodes = new Element[2 * n - 1];
    HuffmanTree(huffnodes,w,n); 
    PrintHuffmanTree(huffnodes,2 * n - 1);

    return 0;
}
  • 打印的结果:
[root@localhost DataStruct]# g++ huffmantree.cc -std=c++11 
[root@localhost DataStruct]# ./a.out 
m1 : 6   m2 : 4
m1 : 2   m2 : 8
m1 : 3   m2 : 1
m1 : 5   m2 : 9
m1 : 0   m2 : 10
m1 : 11   m2 : 7
m1 : 12   m2 : 13
index   weight  parent  lchild  rchild  
0       15      12      -1      -1      
1       9       10      -1      -1      
2       7       9       -1      -1      
3       8       10      -1      -1      
4       4       8       -1      -1      
5       11      11      -1      -1      
6       3       8       -1      -1      
7       31      13      -1      -1      
8       7       9       6       4       
9       14      11      2       8       
10      17      12      3       1       
11      25      13      5       9       
12      32      14      0       10      
13      56      14      11      7       
14      88      -1      12      13  

note:可以将其改进写成链表的方式。

文件压缩的设计

  1. 统计文件A中的各个字符出现的频率
  2. 以统计出来的频率作为权值,创建哈夫曼树,为每一个字符创建其对应的哈夫曼编码(左为0,又为1的原则)
  3. 最后一次将文件A中每个字符对应的哈夫曼编码按二进制位保存在文件B中

哈希法:以ascii码为例,将ascii码的数值作为索引,创建节点可以提高查询速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值