我想每個計算機專業的學生或多或少都接觸過哈夫曼編碼,資料結構中的老問題了。大體就是給出一些字元,和這些字元的出現頻率,讓你為這些字元設計乙個二進位制編碼,要求頻率最高的字元的編碼最短。解決的方法是構造一棵哈夫曼樹(二叉樹),其基本思路是,每次從這些字元中挑出兩個頻率最低的,然後構造乙個新的結點,使新結點的左右孩子指標分別指向那兩個節點。我想這個大家都很清楚了,我就不多說了。主要講下這次我用c++實現時遇到的問題。首先,我定義了乙個哈夫曼樹結點:
class hnode
hnode* left;
hnode* right;
string data; //儲存的字串
int value; //字串出現的次數 };
bool operator >(hnode n1,hnode n2)
因為只是演算法課的小作業,所以我也不準備為hnode定義完整的二叉樹操作,僅僅只是存放資料的物件,所以只有乙個建構函式,並且所有的data member都是公有的。
這此寫這個演算法會遇到**煩,主要因為是用了std::priority_queue容器。當時考慮到在哈夫曼中要每次挑選兩個頻率最小(即出現次數最小,我那個hnode裡的value是出現的次數),很自然的就想到了std::priority_queue容器,優先佇列每次都會彈出佇列中權值最高的元素,這個特性無疑是實現哈夫曼演算法的最佳選擇。然而因為第一次用std::priority_queue容器,結果出了不少問題,好在最後都一一解決,也學到了不少東西。
初步的設想是這樣的,先把所有的hnode物件都壓入優先佇列中去,然後每次彈出兩個,組成乙個新的結點,再把新的結點壓入佇列,重複這一步驟,當佇列中只有乙個元素時,哈夫曼樹也就完成了。像這樣:(是錯的,可別學)
while(...)
然而遭遇的第乙個問題是,stl的所有容器的的插入都是基於by value語義的,也就是要生成乙個物件的副本放在容器中。這樣的後果就是hnode的left,right指標都指到不知道什麼地方去了。大家可以稍微畫幾個圖試一下,就知道出了什麼問題了。考慮一下後,發現如果佇列裡存放hnode的指標,就不會出現這個問題了,於是改寫成:
hnode* maketree(priority_queuepq)
p2 = pq.top();
pq.pop();
r =new hnode;
r->left = p1;
r->right = p2;
r->value = p1->value +p2->value;
pq.push(r); }
return null; }
然而馬上遭遇了第二個問題。std::priority_queue在判斷優先關係的時候,直接比較指標的位址,而不是指標指向的物件的大小關係。而指標不是類,我沒辦法重寫指標的比較操作。程式陷入了困境之中。std::priority_queue預設使用greater<>模板來生成乙個function object來對元素進行比較,我試圖為greater<>寫乙個hnode*的特化版本來改變優先佇列對hnode*的比較,然而也沒有成功。山重水複疑無路之時,突然想到為什麼不直接為優先佇列寫乙個function object來替代greater<>不就可以了嗎?趕快寫下如下**:
struct phnodecomp
};
然後把std::priority_queue的申明變為:
priority_queue,phnodecomp > pq;
終於把這個問題給解決了。看樣子僅從書本上獲得的知識是不牢靠的,一定要自己實踐了才會有真正的認識。
哈夫曼樹與哈夫曼編碼(C 實現)
1 對給定的n個權值構成n棵二叉樹的初始集合f 其中每棵二叉樹ti中只有乙個權值為wi的根結點,它的左右子樹均為空。2 在f中選取兩棵根結點權值最小的樹作為新構造的二叉樹的左右子樹,新二叉樹的根結點的權值為其左右子樹的根結點的權值之和。3 從f中刪除這兩棵樹,並把這棵新的二叉樹同樣以公升序排列加入到...
哈夫曼樹和哈夫曼演算法
1 最優二叉樹 具有最小加權路徑長度的二叉樹 2 哈夫曼演算法 由哈夫曼給出,用於構造最優二叉樹的演算法 3 哈夫曼樹 用哈夫曼演算法構造的最優二叉樹 4 哈夫曼演算法 很熟,所以就略了 5 函式createhfmtree的步驟 首先,構造n棵哈夫曼樹的物件,每棵樹只有乙個權值為w i 的根節點,且...
哈夫曼編碼演算法實現
哈夫曼編碼實現huffmantree.c include include includetypedef struct htnode,huffmantree 動態分配陣列儲存哈夫曼樹 typedef char huffmancode 動態分配陣列儲存哈夫曼編碼 void select huffmant...