貪心 哈夫曼編碼 哈夫曼樹

2021-08-09 02:53:26 字數 2609 閱讀 2327

解決問題

哈夫曼樹

眾所周知,計算機以01串來儲存和運算。

所以,如果我們想要存乙個字元或漢字,例如a,計算機會將它變為乙個01串,這個串就是a的編碼。

如果我們輸入了乙個詞:cat。

如果a的編碼是1,c的編碼是10,t的編碼是11,那麼「cat」對應的編碼就是「10111」。好了,那麼問題來了:10111這個串既是「cat」,也可以理解為「cta」,到底是哪種?如果計算機有龐大的詞庫,或許它會知道沒有「cta」這個詞,但是這樣的複雜度就太高了。所以,在編碼時我們要注意不能有歧義。

歧義的問題先讓它掛一會,先看長度的問題。

為了減少長度,我們可以用變長碼來優化。

什麼意思?會改變長度的編碼?當然不可能,變長碼的意思是:給出現頻率高的字元用短的編碼,給出現頻率低的用長的編碼。這樣可以有效優化總編碼太長的問題。

何以見得「有效」,我們舉個栗子:ab

cdef

出現次數(千次)

4513

121695

定長碼000

001010

011100

101變長碼

0101

100111

1101

1100

定長碼所需空間:3 * ( 45 + 13 + 12 + 16 + 9 + 5 ) = 300 千位

變長碼所需空間:1 * 45 + 3 * ( 13 + 12 + 16) + 4 * ( 9 + 5) = 227 千位

減少了多少自己算。

看編碼是否會有歧義,其實就是看有沒有字元的編碼是另乙個字元編碼的字首。

我們巧妙地使用二叉樹編碼來避免這個問題:

將待編碼的字元當做葉子節點,構造一棵這樣的二叉樹:

(這棵樹有些不平衡,右子樹還可以大些)

將所有的左兒子的邊賦為1(或0),所有右兒子的邊賦為0(或1),會發現,根節點到每個葉節點的路徑的01串不可能成為某個串的字首(因為0的存在),也就避免了歧義的問題。

這個圖中,a的編碼就是111,b的編碼為110,c的編碼為10,d的編碼為0。

哈夫曼(huffman)樹,也叫最優二叉樹,就是上面的二叉樹,並使編碼長度最優。

相信有人能看出:上面的二叉樹每個葉節點到根的長度不同(廢話),也就是說,每個字元的編碼長度不同,這不正好就是變長碼了嗎?

所以,直接把出現頻率低的放在下面,出現頻率高的放在上面不就好了!

如果你看到這裡就去實現且成功了,我給你出彩。

如果你沒有成功,不要怕,讓我們直接get哈夫曼前輩的演算法:

構造一棵哈夫曼樹

①每個字元自成一棵樹,根為它自己且權值為它的出現次數;

②將所有樹按根節點的權值從小到大排序;

③取出根節點權值最小的兩顆樹,合併起來,並將它們合併後根節點的權值賦為它們合併前根節點的權值之和;

④將第③步中合併得到的樹的根節點放入②的序列中並維護其有序性;

⑤重複③、④直到只剩下一棵樹。

看起來很多,其實很簡單:用優先佇列維護權值的從小到大,每次取2次top並pop掉,然後合併這兩個top,將合併後的節點放入佇列,重複執行n-1次(n為字元數)。

樹建好了,輸出編碼就很簡單了,從根開始遞迴,把途經的路徑存下來,一旦到達葉子節點就輸出。

你們最愛的**:

//輸入n,接下來輸入n個數,代表出現次數,然後輸出每個葉子節點的編碼

#include

#include

#include

#include

#include

using

namespace

std;

#define maxn 50

#define maxt 100

struct node

//優先佇列比較方式

}tree[maxt+5];

int n,size,root;

char ans[maxn+5];

priority_queueq;

void unite(node x,node y)//合併兩個節點

void print(int u,int len)//len記錄長度

len++;

ans[len]='0';

print(tree[u].lc,len);

ans[len]='1';

print(tree[u].rc,len);

ans[len]=0;

}int main()

if(n==1) printf("0");//特殊處理

size=n;

for(int i=1;i//注意top一次就要pop一次,不然取出來的節點是一樣的

node u2=q.top();q.pop();

unite(u1,u2);

}for(int i=1;i<=size;i++)//找根

if(!tree[i].f)

root=i;

print(root,0);

}

哈夫曼編碼 哈夫曼樹

1.定義 哈夫曼編碼主要用於資料壓縮。哈夫曼編碼是一種可變長編碼。該編碼將出現頻率高的字元,使用短編碼 將出現頻率低的字元,使用長編碼。變長編碼的主要問題是,必須實現非字首編碼,即在乙個字符集中,任何乙個字元的編碼都不是另乙個字元編碼的字首。如 0 10就是非字首編碼,而0 01不是非字首編碼。2....

哈夫曼樹 哈夫曼編碼

定義從a結點到b結點所經過的分支序列為從a結點到b結點的路徑 定義從a結點到b結點所進過的分支個數為從a結點到b結點的路徑長度 從二叉樹的根結點到二叉樹中所有結點的路徑長度紙盒為該二叉樹的路徑長度 huffman樹 帶權值路徑長度最小的擴充二叉樹應是權值大的外界點舉例根結點最近的擴充二叉樹,該樹即為...

哈夫曼編碼 哈夫曼樹

哈夫曼樹是乙個利用權值進行優化編碼的乙個比較奇怪的樹,他的實現比較簡單,用途也比較單一。哈夫曼樹的實現,實現要求 通過哈夫曼樹可以保證在編碼過程中不會出現例如 1000和100這樣的編碼規則,否則就會編碼失敗,因為1000和100在某些情況下的編碼會一模一樣。通過哈夫曼樹可以保證權值大的值進行編碼時...