哈夫曼树编码

 


对输入的英文大写字母进行统计概率 然后构建哈夫曼树,输出是按照概率降序排序输出Huffman编码
输入
  大写字母个数 n 第一个字母 第二个字母 第三个字母 ... 第n个字母
 
输出
 字母1 出现次数 Huffman编码 
 字母2 出现次数 Huffman编码 
 字母3 出现次数 Huffman编码 
 … 
 字母n 出现次数 Huffman编码

Sample In
10
I I U U U I U N U U
 
Sample Out
U 6 1
I 3 01
N 1 00

解决此题首先要明白HuffmanTree的构造原理

首先来看一个简单的例子:
把某个班同学百分制的成绩转换成5分制,规则如下,
 
90~100     5
80~90       4
70~80       3
60~70       2
<60           1
 
看起来很容易实现
即 if (score<60)
     grade=1;
     else if(score<70)
     grade=2;
     else if(score<80)
     grade=3;
     else if(score<90)
     grade=4;
     else
     grade=5;
但是如果这个班的同学大多数都取得了90分以上的好成绩,那么前面4步的判断是很没必要且费时的。
很明显,这种算法在面对大量数据的时候是比较不合理的。
那么如何优化算法呢?
假定我们目前已经知道了这个班成绩的分布律
 

成绩

<60

60~70

70~80

80~90

90~100

比例

0.05

0.15

0.33

0.27

0.20

 
如果用刚才的办法,则算法的效率是多少呢,用比例乘上判断的次数0.05*1+0.15*2+0.33*3+0.27*4+0.20*4=3.22
我们稍微修改一下算法,先判断比例最大的,
即   if(score<80)
    {
        if(score<70)
            if(score<60)
            grade=1;
        else
            grade=2;
        else
            grade=3;
    }
    else if(score<90)
        grade=4;
    else
        grade=5;
      
改良后的算法效率为0.05*3+0.15*3+0.33*2+0.27*2+0.2*2=2.2
很明显,改良后的算法效率增加了很多。
由树的定义可以把刚才的程序抽象成下图所示的"树":
 
 
那么如何构造一个效率更好或者最好的搜索树呢,这就是哈夫曼树要解决的问题。
构造HuffmanTree思想:把权值(频率)从小到大排序,把权值最小的两颗二叉树合并
比如现有权值为1,2,3,4,5的节点(已经排好序)。
1.选择最小的两个,即1和2,合并,权值之和为3,
2.从刚才合并好的3和剩下的3,4,5里选择两个最小的,即3和3,合并,权值之和为6
3.从6,4,5里选择两个最小的,即4和5,合并,权值之和为9
4.将6和9合并,权值之和为15
下图为形成的哈夫曼树:
 
对于上面的问题,我们的思路之一应该是这样
1.统计相同字符出现的次数并记录之
2.根据统计好的结果构造哈夫曼树
3.获得哈夫曼编码
4.按题目要求格式打印
那么写出代码就是轻而易举的事情了:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;


template<class T>
struct StaFrequency
{
    T data;
    int times;
    StaFrequency();
    StaFrequency(T data,int times) {this->data=data;this->times=times;}
};
template<class T>
struct TriNode
{
    T data;
    int parent,left,right;
};
template<class T>
class HuffmanTree
{
private:
    int leafNum;
    TriNode<int> *huftree;
    char **hufcodes;
    void createHuffmanTree(T weight[],int n);
    void getHuffmanCode();
public:
    HuffmanTree(T weight[],int n);
    ~HuffmanTree() {delete []huftree;delete []hufcodes;};
    void print(int i);
};
const int Max_Weight=9999;
template <class T>
HuffmanTree<T>::HuffmanTree(T weight[],int n)
{
    createHuffmanTree(weight,n);
    getHuffmanCode();
}
//构造哈夫曼树
template <class T>
void HuffmanTree<T>::createHuffmanTree(T weight[],int n)
{
    leafNum=n;
    huftree=new TriNode<int>[2*n-1];
    int i;
    for(i=0;i<n;i++)
    {
        huftree[i].data=weight[i];
        huftree[i].parent=huftree[i].left=huftree[i].right=-1;
    }
    for(i=0;i<n-1;i++)
    {
        int min1,min2,x1,x2;
        min1=min2=Max_Weight;
        x1=x2=-1;
        for(int j=0;j<n+i;j++)
        {
            if(huftree[j].data<min1&&huftree[j].parent==-1)
            {
                min2=min1;
                x2=x1;
                min1=huftree[j].data;
                x1=j;
            }
            else if(huftree[j].data<min2&&huftree[j].parent==-1)
            {
                min2=huftree[j].data;
                x2=j;
            }
        }
        huftree[x1].parent=n+i;
        huftree[x2].parent=n+i;
        huftree[n+i].data=huftree[x1].data+huftree[x2].data;
        huftree[n+i].parent=-1;
        huftree[n+i].left=x1;
        huftree[n+i].right=x2;
    }
}
//获得哈夫曼编码
template <class T>
void HuffmanTree<T>::getHuffmanCode()
{
    int n=leafNum;
    hufcodes=new char *[n];
    for(int i=0;i<n;i++)
    {
        char * code=new char[n];
        code[n-1]='\0';
        int start=n-1;
        int child=i;
        int parent=huftree[child].parent;
        while(parent!=-1)
        {
            start--;
            if(huftree[parent].left==child)
                code[start]='0';
            else
                code[start]='1';
            child=parent;
            parent=huftree[child].parent;
        }
        hufcodes[i]=code+start;
    }
}
//打印哈夫曼编码
template <class T>
void HuffmanTree<T>::print(int i)
{
    cout<<hufcodes[i]<<endl;
}
int main()
{
    int m;
    cin>>m;
    char weight[m];
    for(int i=0;i<m;i++)
        cin>>weight[i];
int i=0,n=0,sum=0,num=0,x=0;
int s[m];
char w[m];
bool flag;
for(i=0;i<m;i++)
{
   flag=true;
   sum=0;
   for(int j=i-1;j>=0;j--)//检测是否有已经算过的字母
   {
      if(weight[j]==weight[i])
      {
      flag=false;
      break;
  }
}
for(n=i;n<m&&flag;n++)//若上一步没有,则算相同的个数
{
if(weight[i]==weight[n])
{
sum++;
}
}
if(flag) //存储相同字符及个数
   {
  s[num++]=sum;
  w[x++]=weight[i];
}
}
    //就写个最简单的冒泡排序吧
    for(int k=0;k<num;k++)
    {
        for(int j=0; j<num-1-k; j++)
        {
            if(s[j]<s[j+1])
            {
                swap(s[j],s[j+1]);
                swap(w[j],w[j+1]);
            }


        }
    }
    HuffmanTree<int> htree(s,num);
    for(int i=0;i<num;i++)
    {
        cout<<w[i]<<" "<<s[i]<<" ";
        htree.print(i);
    }
    return 0;
}

 

posted @ 2017-11-02 19:44  Nikki_o3o  阅读(554)  评论(0编辑  收藏  举报