當前位置:首頁 » 操作系統 » 哈夫曼編碼演算法實現

哈夫曼編碼演算法實現

發布時間: 2025-03-17 11:23:31

Ⅰ 哈夫曼編碼原理

赫夫曼碼的碼字(各符號的代碼)是異前置碼字,即任一碼字不會是另一碼字的前面部分,這使各碼字可以連在一起傳送,中間不需另加隔離符號,只要傳送時不出錯,收端仍可分離各個碼字,不致混淆毀亮余。

哈夫曼編碼,又稱霍夫曼編碼,是一種編碼方式,哈夫曼編碼是可變字長編碼(VLC)的一種。Huffman於1952年提出一種編碼方法,纖滾該方法完全依據字元出現概率來構造異字頭的平均長度最短的碼字,有時稱之為最佳編碼,一般就叫做Huffman編碼。

(1)哈夫曼編碼演算法實現擴展閱讀

赫夫曼編碼的具體方法:先按出現的概率大小排隊,把兩個最小的概率相加,作為新的概率
和剩餘的概率重新排隊,再把最鍵孫小的兩個概率相加,再重新排隊,直到最後變成1。

每次相
加時都將「0」和「1」賦與相加的兩個概率,讀出時由該符號開始一直走到最後的「1」,
將路線上所遇到的「0」和「1」按最低位到最高位的順序排好,就是該符號的赫夫曼編碼。

例如a7從左至右,由U至U″″,其碼字為1000;

a6按路線將所遇到的「0」和「1」按最低位到最高位的順序排好,其碼字為1001…

用赫夫曼編碼所得的平均比特率為:Σ碼長×出現概率

上例為:0.2×2+0.19×2+0.18×3+0.17×3+0.15×3+0.1×4+0.01×4=2.72 bit

可以算出本例的信源熵為2.61bit,二者已經是很接近了。

Ⅱ 哈夫曼編碼演算法的實現

在網上看到一個,剛好用到,我試過的,正確
#include <stdio.h>
#include<malloc.h>
#include <string.h>
#include<fstream>
#include<iostream>
using namespace std;

typedef struct {
unsigned int weight;
char ch1;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;

typedef char **HuffmanCode;

typedef struct {
char ch;
char code[7];
}codenode,*code;

void select(HuffmanTree HT,int n,int & s1,int &s2){ //從哈夫曼樹中選擇出最小的兩個節點
for(int i=1;i<=n;i++)
if(!HT[i].parent){
s1=i; break;
}
for(i++;i<=n;i++)
if(!HT[i].parent){
s2=i; break;
}
if(HT[s1].weight-HT[s2].weight){
int temp; temp=s1; s1=s2; s2=temp;
}
for(i=1;i<=n;i++) //對數組進行遍歷,尋找最小的兩個節點
if(!HT[i].parent){
if(HT[i].weight<HT[s1].weight){
s2=s1; s1=i;
}
else if(HT[i].weight<HT[s2].weight&&i!=s1)
s2=i;
}
}

void prin(){ //終端輸出選擇菜單
cout<<"----------------------------------------------------\n\n"
<<" ∣ I---創建哈夫曼樹 ∣\n"
<<" ∣ ∣\n"
<<" ∣ E---文件編碼 ∣\n"
<<" ∣ ∣\n"
<<" ∣ D---文件解碼 ∣\n"
<<" ∣ ∣\n"
<<" ∣ P---列印代碼文件 ∣\n"
<<" ∣ ∣\n"
<<" ∣ T---印哈夫曼樹 ∣\n"
<<" ∣ ∣\n"
<<" ∣ O---哈夫曼樹的存儲結構 ∣\n"
<<" ∣ ∣\n"
<<" ∣ Q---退出 ∣\n"
<<"\n-----------------------------------------------------\n\n";
printf("選擇菜單功能選項:");
}

void output (HuffmanTree th,int n){ //輸出哈夫曼樹的存儲結構
int i=0;
cout<<"序號"<<" "<<"字元"<<" "<<"雙親"<<" "<<"左孩子"<<" "<<"右孩子"<<" "<<"權值"<<endl;
for(;i<2*n-1;i++){
th++;
cout<<i<<" "<<th->ch1<<" "<<th->parent<<" "<<th->lchild<<" "<<th->rchild<<" "<<th->weight <<endl;
}
}

void initial(HuffmanTree &HT,HuffmanCode &HC,int w[],int &n,char ch[],int &k){ //創建哈夫曼樹
cout<<"----------------------------------------------------\n\n"
<<" ∣ 1---自定義 ∣\n"
<<" ∣ ∣\n"
<<" ∣ 2---編碼課本測試數據 ∣\n"
<<" ∣ ∣\n"
<<" ∣ 3---編碼源程序 ∣\n"
<<"\n-----------------------------------------------------\n\n";
printf("選擇菜單功能選項:");
scanf("%d",&k);
if(k==1){
printf("輸入需要編碼的字元總數: ");
scanf("%d",&n);
printf("\n輸入需要編碼字元的權值:\n");
for(int d=0;d<n;d++) {
scanf("%d",&w[d]);
}
printf("\n輸入需要編碼的字元串: ");
scanf("%s",ch);
}
else if(k==2){
ifstream fin2 ("test.txt");
fin2>>n;
for(int d=0;d<n;d++)
fin2>>w[d];
fin2>>ch;
fin2.close();
}
else if(k==3){
ifstream fin1 ("input.txt");
fin1>>n;
for(int d=0;d<n;d++)
fin1>>w[d];
fin1>>ch;
fin1.close();
}
if(n<=1)
return;
int s1,s2,i,num=2*n-1;
HuffmanTree p;
HT=(HuffmanTree)malloc((num+1)*sizeof(HTNode));
for(p=HT+1,i=1;i<=n;i++,p++){
p->weight=w[i-1]; p->lchild=0; p->parent=0; p->rchild=0; p->ch1 =ch[i-1];
}
for(;i<=num;p++,i++){
p->weight=0; p->lchild=0; p->parent=0; p->rchild=0; p->ch1 ='$';
}
for(i=n+1;i<=num;i++){
select(HT,i-1,s1,s2);
HT[s1].parent=i; HT[s2].parent=i; HT[i].lchild=s1;
HT[i].rchild=s2; HT[i].weight=HT[s1].weight+HT[s2].weight;
}
HC=(HuffmanCode)malloc((n+1)*sizeof(char *));
char * temp=(char *)malloc(n*sizeof(char));
temp[n-1]='\0';
for(i=1;i<=n;i++){
int start=n-1;
for(int f=HT[i].parent,h=i;f;h=f,f=HT[f].parent)
if(HT[f].lchild==h)
temp[--start]='0';
else
temp[--start]='1';
HC[i]=(char *)malloc((n-start)*sizeof(char));
strcpy(HC[i],&temp[start]);
}
ofstream fout ("hfmTree.txt");
fout<<ch<<endl;
for(int j=1;j<=n;j++)
fout<<HC[j]<<endl;
fout.close();
free(temp);
}

void encoding(int n,int select){ //編碼:對文件TobeTran.txt進行解碼
char a[100],b[100][20];
ifstream fin ("hfmTree.txt");
fin>>a;
for(int j=0;j<n;j++) fin>>b[j];
fin.close();
ifstream fin1 ("course.txt");
ifstream fin2 ("sorse.txt");
ifstream fin3 ("ToBeTran.txt");
char s[1000];
if(select==3)
fin2>>s;
else if(select==2)
fin1>>s;
else fin3>>s;
ofstream fout ("CodeFile.txt");
while(s[0]!='\0'){
for(int i=0;s[i]!='\n'&&s[i]!='\0'&&i<30;i++ ){
for(int g=0;a[g]!=s[i];g++) ;
fout<<b[g];
}
fout<<'\n';
if(select==3)
fin2>>s;
else if(select==2)
fin1>>s;
else fin3>>s;
}
fin3.close();
fin2.close();
fin1.close();
fout.close();
}

void decoding(HuffmanTree ht,int n){ //解碼:對CodeFile.txt文件進行解碼
ifstream fin ("CodeFile.txt");
ofstream fout ("TextFile.txt");
char s[500];
fin>>s;
HuffmanTree head=ht+2*n-1;
int i=0;
while(s[0]!='\0'){
while(s[i]!='\0'){
if(s[i]=='1') head=ht+head->rchild;
else if(s[i]=='0') head=ht+head->lchild;
if((head->lchild)==0&&(head->rchild) ==0) {
fout<<(head->ch1);
head=ht+2*n-1;
}
i++;
}
fout<<' ' ;
i=0;
fin>>s;
}
fin.close();
fout.close();
}

void Print(){ //列印代碼文件,顯示在終端,每行50個代碼
ifstream fin ("CodeFile.txt");
char s[2000];
int j=0;
int i=1;
fin>>s;
ofstream fout ("CodePrin.txt");
while(s[0]!='\0'){
for(;s[j]!='\0';j++){
printf("%c",s[j]);
fout<<s[j];
if(i%50==0){
fout<<endl;
printf("\n");
}
i++;
}
j=0;
fin>>s;
}
fin.close();
printf("\n");
fout.close();
}

void printTree( HuffmanTree node,HuffmanTree node1, int level ) { //列印哈夫曼樹形(在參數的傳遞上,是文科給自己提出的意見才很好的解決了之後的操作難題^^)
if( node == NULL ) return;
if( node1->rchild!=0) {
printTree( node,node+node1->rchild, level + 1 );
}
fstream fout ;
fout.open ("TreePrint.txt",ios::in | ios::out|ios::ate);//這個挺有用的:在文件末尾加入內容
for( int i = 0; i < level; i++ ) {
fout<<"|……";
printf( "……");
}
fout<<node1->weight<<endl;
printf( "%d\n", node1->weight );
if( node1->lchild!=0 ) {
printTree( node,node+node1->lchild, level + 1 );
}
fout.close();
}

void main(){
int select;
int n;
char ch[100];
int w[100];
HuffmanTree HT=NULL;
HuffmanCode hc=NULL;
prin();
char c='I';
scanf("%c",&c);
while(c!='Q'){
switch(c){
case 'I':
initial(HT,hc,w,n,ch,select);
prin();
break;
case 'E':
encoding(n,select);
prin();
break;
case 'D':
decoding(HT,n);
prin();
break;
case 'P':
Print();
prin();
break;
case 'T':
printTree(HT,HT+2*n-1,1);
prin();
break;
case 'O':
output(HT,n);
prin();
break;
}
scanf("%c",&c);
}

}

Ⅲ 哈夫曼編碼(貪心演算法)

參考: 哈夫曼編碼

哈夫曼編碼是一種十分有效的編碼方法,廣泛應用於 數據壓縮
通過採用 不等長 的編碼方式,根據 字元頻率的不同 ,選擇 不差派拿同長度的編碼 ,對頻率 越高 的字元採用 越短 的編碼實現數據的高度壓縮。
這種對頻率越高的字元採用越短的編碼來編碼的方式應用的就是貪心演算法的思想。

下面看一個例子:
假如我們有虛搭一個包含1000個字元的文件,每個字元佔1個byte(1byte=8bits),則存儲這100個字元一共需要8000bits。這還是有一些大的
那我們統計一下這1000個字元中總共有多少種字元,原來需要8bit來表示一個字元,如果使用更少的位數來表示這些字元,則可以減少存儲空間。
假設這1000個字元中總共有a、b、c、d、e、f共6種字元,使用使用3個二進制位來表示的話,存儲這1000個字元就只需要3000bits,比原來更節省存儲空間。

或許還可以再壓縮一下:
根據字元出現的 頻率 給與字元 不等長 的編碼,頻率越高的字元編碼越短,頻率越低的字元編碼越長。
它不能像等長編碼一樣直接按固定長度去讀取二進制位,翻譯成字元,為了能夠准確讀取翻譯字元,它要求一個字元的編碼不能是另外一個字元的前綴。

假設a、b、c、d、e、f這6個字元出現的頻率依次降低,則我們可以給與他們這樣的編碼

假如字元的出現頻率如圖所示,按照這樣的編碼表示的話,總位數如圖,一共2100bits,更加節省空間了

貪心策略:頻率小的字元,優先入隊。

步驟:
1.將每一個字元作為節點,以出現頻率大小作為權重,將其都放入 優先隊列 中(一個最小堆);
2.每次出隊兩個節點並創建一個父節點,使其權值為剛剛出隊的節點的權值和,並且為兩個節點的父節點(合並)。然後將這個樹入隊。
3.重復操作2,直到隊列中只有一個元素(此時這個元素表示形式應該為一個樹)時,完成創建。

創建好了樹,該怎麼編碼呢?
我們對一個哈夫曼樹,從父節點開始的所有節點,往左邊標0,右邊標1。那麼到達葉子節點的順次編碼就可以找到了。

C:字元集合
Q:優先隊列
EXTRACT-MIN:傳入一羨山個隊列,出隊最小的元素
INSERT:將z插入到Q中

當for循環結束之後,此時隊列中只有一個元素,就是我們需要的哈夫曼樹,最後返回此樹即可。

假設T樹已經是一個最優的樹,假設x、y的頻率小於等於最低處的a、b,然後交換x、a,y、b。

計算代價是否發生變化。
比如這里比較 T 變成 T 』 後代價是否變化,發現代價變小或不變。

同理T』到T』』,又因為T本來假設就是最優的,所以只能相等
所以T』』也應該符合條件,即貪婪演算法,每次取最小的兩個節點出來這種做法是正確的

熱點內容
蝸牛游戲安卓手機怎麼更換賬號 發布:2025-03-17 13:41:49 瀏覽:322
為什麼人買一個蘋果一個安卓 發布:2025-03-17 13:36:59 瀏覽:439
三星手機簡訊在那個文件夾 發布:2025-03-17 13:31:51 瀏覽:194
安卓皇帝隱藏劇情在哪裡 發布:2025-03-17 13:18:53 瀏覽:507
新版安卓為什麼不兼容 發布:2025-03-17 13:18:49 瀏覽:483
s3哪個配置性價比高 發布:2025-03-17 13:06:09 瀏覽:320
氣體壓縮能量 發布:2025-03-17 13:00:16 瀏覽:78
壓縮油19 發布:2025-03-17 12:25:29 瀏覽:858
linux上網代理 發布:2025-03-17 12:23:56 瀏覽:361
c是高級語言嗎 發布:2025-03-17 12:16:31 瀏覽:525