原文:
一步一步寫演算法(之哈夫曼樹 上)
在資料傳輸的過程當中,我們總是希望用盡可能少的頻寬傳輸更多的資料,哈夫曼就是其中的一種較少頻寬傳輸的方法。哈夫曼的基本思想不複雜,那就是對於出現頻率高的資料用短位元組表示,對於頻率比較低得資料用長位元組表示。
比如說,現在有4個資料需要傳輸,分別為a、b、c、d,所以一般來說,如果此時沒有考慮四個資料出現的概率,那麼我們完全可以這麼分配,平均長度為2,
/*
* a - 00 b - 01
* c - 10 d - 11
*/
但是,現在條件發生了改變,四個資料出現的頻率並不一樣,分別為0.1/0.2/0.3/0.4。那麼這時候應該怎麼分配長度呢,其實也簡單。我們只要把所有資料按照頻率從低到高排列,每次取前兩位合併成新的節點,再把這個新節點放到佇列中重新排序即可。新節點的左結點預設設為1,右結點預設設為0。然後重複上面的過程,直到所有的節點都合併成乙個節點為止。如果應用到實際的示例中,合併的過程應該是這樣的,
第一步,首先合併a和b,因為a和b是概率最小的
/*
* * total_1(0.3) c (0.3) d(0.4)
* / \
* a(0.1) b(0.2)
*/
第二步,接著合併total_1和c,/*
* total_2 (0.6)
* / \
* total_1(0.3) c (0.3) d(0.4)
* / \
* a(0.1) b(0.2)
*/
最後一步,合併total_2和d,/*
* final (1.0)
* / \
* d (0.4) total_2 (0.6)
* / \
* total_1(0.3) c (0.3)
* / \
* a(0.1) b(0.2)
*/
所以按照上面的生成樹,資料的編號應該這麼安排,
/*
* a - 011 b - 010
* c - 00 d - 1
*/
看上去a和b的長度還增加了,但是d的長度減少了。那麼整個資料的平均長度有沒有減少呢?我們可以計算一下。3 * 0.1 + 3 * 0.2 + 2 * 0.3 + 0.4 = 1.9 < 2。我們發現調整後的資料平均長度比原來減少了近(2 - 1.9)/2 * 100% = 10 %,這可是巨大的發現啊。
為了完成整個哈夫曼樹的建立,我們還需要定義乙個資料結構:
typedef struct _huffman_node
huffman_node;
其中str記錄字元,frequency記錄字元出現的頻率, symbol記錄分配的資料,左子樹為1、右子樹為0,left為左子樹,right為右子樹,parent為父節點。接下來,我們從建立huffman結點開始。huffman_node* create_new_node(char str, double frq)
【未完,待續】
一步一步寫演算法(之哈夫曼樹 上)
在資料傳輸的過程當中,我們總是希望用盡可能少的頻寬傳輸更多的資料,哈夫曼就是其中的一種較少頻寬傳輸的方法。哈夫曼的基本思想不複雜,那就是對於出現頻率高的資料用短位元組表示,對於頻率比較低得資料用長位元組表示。比如說,現在有4個資料需要傳輸,分別為a b c d,所以一般來說,如果此時沒有考慮四個資料...
一步一步寫演算法(之 A 演算法)
在前面的部落格當中,其實我們已經討論過尋路的演算法。不過,當時的示例圖中,可選的路徑是唯一的。我們挑選乙個演算法,就是說要把這個唯一的路徑選出來,怎麼選呢?當時我們就是採用窮盡遞迴的演算法。然而,今天的情形有點不太一樣了。在什麼地方呢?那就是今天的路徑有n條,這條路徑都可以達到目的地,然而我們在挑選...
一步一步寫演算法(之 A 演算法)
在前面的部落格當中,其實我們已經討論過尋路的演算法。不過,當時的示例圖中,可選的路徑是唯一的。我們挑選乙個演算法,就是說要把這個唯一的路徑選出來,怎麼選呢?當時我們就是採用窮盡遞迴的演算法。然而,今天的情形有點不太一樣了。在什麼地方呢?那就是今天的路徑有n條,這條路徑都可以達到目的地,然而我們在挑選...