用C++ std::priority_queue 實現哈夫曼演算法

來源:互聯網
上載者:User

我想每個電腦專業的學生或多或少都接觸過哈夫曼編碼,資料結構中的老問題了。大體就是給出一些字元,和這些字元的出現頻率,讓你為這些字元設計一個二進位編碼,要求頻率最高的字元的編碼最短。解決的方法是構造一棵哈夫曼樹(二叉樹),其基本思路是,每次從這些字元中挑出兩個頻率最低的,然後構造一個新的結點,使新結點的左右孩子指標分別指向那兩個節點。我想這個大家都很清楚了,我就不多說了。主要講下這次我用C++實現時遇到的問題。首先,我定義了一個哈夫曼樹結點:

class hNode
{
public:
friend bool operator > (hNode n1,hNode n2); //定義了大於符號,供優先隊列排列使用
 hNode(string d="",int i=0,hNode* l = NULL,hNode* r =NULL):left(l),right(r),data(d),value(i){}
 hNode* left;
 hNode* right;
 string data; //儲存的字串
 int value; //字串出現的次數
};

bool operator >(hNode n1,hNode n2)
{
 return n1.value > n2.value;
}
因為只是演算法課的小作業,所以我也不準備為hNode定義完整的二叉樹操作,僅僅只是存放資料的對象,所以只有一個建構函式,並且所有的data member都是公有的。

這此寫這個演算法會遇到大麻煩,主要因為是用了std::priority_queue容器。當時考慮到在哈夫曼中要每次挑選兩個頻率最小(即出現次數最小,我那個hNode裡的value是出現的次數),很自然的就想到了std::priority_queue容器,優先隊列每次都會彈出隊列中權值最高的元素,這個特性無疑是實現哈夫曼演算法的最佳選擇。然而因為第一次用std::priority_queue容器,結果出了不少問題,好在最後都一一解決,也學到了不少東西。

初步的設想是這樣的,先把所有的hNode對象都壓入優先隊列中去,然後每次彈出兩個,組成一個新的結點,再把新的結點壓入隊列,重複這一步驟,當隊列中只有一個元素時,哈夫曼樹也就完成了。像這樣:(是錯的,可別學)

while(...)
{
std::priority_queue<hNode> q;
.....
hNode h1 = q.top();
q.pop();
hNode h2 = q.top();
q.pop();
hNode r;
r.left = h1;
r.right = h2;
r.value = h1.value + h2.value;
q.push(r);
}

然而遭遇的第一個問題是,STL的所有容器的的插入都是基於by value語義的,也就是要產生一個對象的副本放在容器中。這樣的後果就是hNode的left,right指標都指到不知道什麼地方去了。大家可以稍微畫幾個圖試一下,就知道出了什麼問題了。考慮一下後,發現如果隊列裡存放hNode的指標,就不會出現這個問題了,於是改寫成:
hNode* makeTree(priority_queue<hNode*> pq)
{
 hNode* p1 = NULL;
 hNode* p2 = NULL;
 hNode* r = NULL;
 while( !pq.empty())
 {
  p1 = pq.top();
  pq.pop();
  if (pq.empty())
  {
   r = p1;
   return r;
  }
  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
{
  bool operator () (const hNode*& left,const hNode*& right) const
 {
  return left->value > right->value;
 }
};

然後把std::priority_queue的申明變為:
priority_queue<hNode*,vector<hNode*>,phNodeComp > pq;

終於把這個問題給解決了。看樣子僅從書本上獲得的知識是不牢靠的,一定要自己實踐了才會有真正的認識。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.