對輸入的英文大寫字母進行統計機率 然後構建哈夫曼樹,輸出是按照機率降序排序輸出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;
}