貪心演算法;最小堆+哈夫曼樹;列印出每個字母對應編碼;調試了幾次,應該無錯;

來源:互聯網
上載者:User

#include <iostream><br />using namespace std;</p><p>typedef struct HuffmanNode//哈弗曼樹的結點<br />{<br />char letter;//儲存的字元,分葉節點為字母,非分葉節點為#<br />struct HuffmanNode *parent;//父親結點<br />int code;//如果為父結點的左孩子,則為0,右孩子為1<br />}HuffmanNode;</p><p>typedef struct HeapNode//堆結點定義<br />{<br />int rate;//出現頻率<br />HuffmanNode *node;//對應於哈弗曼樹中的結點<br />}HeapNode;</p><p>//需要在初始化堆得時候完成所有葉結點的插入</p><p>class Huffman<br />{<br />private:<br />int heapSize;//堆大小<br />int num;//記錄字元數量<br />HeapNode *heap;//堆數組<br />char *letter;//字元數組<br />int *rate;//字元出現頻度<br />HuffmanNode **arr;//記錄葉結點的數組,列印編碼的時候可以從葉結點回溯向上<br />public:<br />//建構函式<br />Huffman(int numOfLetters)//傳入字元個數<br />{<br />this->heapSize=numOfLetters;//堆大小初始化為字母數<br />num=numOfLetters;//記錄字元數量<br />heap=new HeapNode[numOfLetters+1];//分配堆空間,最多隻需要字元的個數,因為合并的過程刪除2個插入1個<br />letter=new char[numOfLetters+1];//用於儲存字元<br />rate=new int[numOfLetters+1];//用於儲存字元出現頻度<br />arr=new HuffmanNode* [numOfLetters+1];<br />}</p><p>//輸入數組<br />void input()<br />{<br />int i=1;<br />while(i<=heapSize)<br />{<br />cout<<"輸入第"<<i<<"個字元"<<endl;<br />cin>>letter[i];<br />cout<<"輸入第"<<i<<"個字元的頻度"<<endl;<br />cin>>rate[i];<br />//向堆中插入各個結點<br />heap[i].rate=rate[i];<br />heap[i].node=new HuffmanNode;<br />arr[i]=heap[i].node;<br />heap[i].node->parent=NULL;<br />heap[i].node->letter=letter[i];<br />i++;<br />}<br />}</p><p>//求父結點<br />int parent(int i)<br />{<br />return i/2;<br />}<br />//求左孩子<br />int left(int i)<br />{<br />return 2*i;<br />}<br />//求右孩子<br />int right(int i)<br />{<br />return 2*i+1;<br />}</p><p>//交換函數<br />void swap(int i,int j)<br />{<br />//交換結構體數組,需要交換結構體內資料<br />int rate;//用於交換頻度<br />HuffmanNode *p;//用於交換哈弗曼樹的結點指標<br />rate=heap[i].rate;<br />p=heap[i].node;<br />heap[i].rate=heap[j].rate;<br />heap[i].node=heap[j].node;<br />heap[j].rate=rate;<br />heap[j].node=p;<br />}</p><p>//維持堆性質函數,使用前提為左右子書均為最小堆<br />void heapIfy(int i,int localHeapSize)<br />{<br />int l=left(i);<br />int r=right(i);<br />int least=i;<br />//找出i,left(i),right(i)的最小值<br />if(l<=localHeapSize&&heap[least].rate>heap[l].rate)<br />{<br />least=l;<br />}<br />if(r<=localHeapSize&&heap[least].rate>heap[r].rate)<br />{<br />least=r;<br />}<br />//交換heap[i]與heap[least]<br />if(least!=i)<br />{<br />swap(i,least);<br />heapIfy(least,localHeapSize);<br />}<br />}</p><p>//初始化堆(建立堆)<br />void buildHeap()<br />{<br />for(int i=heapSize/2;i>=1;i--)<br />{<br />heapIfy(i,heapSize);<br />}<br />}</p><p>//去掉並返回堆中最小的元素(前提:堆已初始化)<br />HeapNode* extractMin()<br />{<br />if(heapSize>=1)<br />{<br />HeapNode *min;<br />swap(1,heapSize);<br />min=&heap[heapSize];<br />--heapSize;<br />heapIfy(1,heapSize);<br />return min;<br />}<br />else<br />{<br />cout<<"堆中沒有元素"<<endl;<br />return NULL;<br />}<br />}</p><p>//向堆中插入資料(前提:堆已經初始化)<br />void heapInsert(int rate,HuffmanNode *p)<br />{<br />++heapSize;<br />int i=heapSize;</p><p>while(i>1&&heap[parent(i)].rate>rate)<br />{<br />heap[i].rate=heap[parent(i)].rate;<br />heap[i].node=heap[parent(i)].node;<br />i=parent(i);<br />}</p><p>heap[i].rate=rate;<br />heap[i].node=p;<br />}</p><p>//構造哈弗曼樹<br />HuffmanNode* buildTree()<br />{<br />buildHeap();//初始化堆<br />HeapNode *p;//用於臨時儲存最小堆結點<br />HeapNode *q;//用於臨時儲存次小堆結點<br />int count=heapSize;<br />for(int i=1;i<=count-1;i++)<br />{<br />HuffmanNode *tree=new HuffmanNode;// 產生內結點<br />tree->letter='#';//內結點的字元記作#,沒有實際意義<br />p=extractMin();//取堆最小結點<br />q=extractMin();//取堆次小結點<br />p->node->parent=tree;<br />p->node->code=0;<br />q->node->parent=tree;<br />q->node->code=1;<br />heapInsert(p->rate+q->rate,tree);//插入堆中<br />}<br />return extractMin()->node;<br />}<br />//遞迴列印編碼<br />void display()<br />{<br />HuffmanNode *p=buildTree();//哈弗曼樹的根節點地址<br />int i=1;<br />while(i<=num)<br />{<br />cout<<arr[i]->letter<<":";<br />backPrint(arr[i],p);<br />cout<<endl;<br />i++;<br />}<br />}<br />//從葉結點回溯列印code編碼<br />void backPrint(HuffmanNode *p,HuffmanNode *root)<br />{<br />if(p!=root)<br />{<br />backPrint(p->parent,root);<br />cout<<p->code;<br />}</p><p>}</p><p>};</p><p>void main()<br />{<br />Huffman test(5);<br />test.input();<br />test.display();<br />}

 

 

關鍵在於最小堆與哈夫曼樹的結構體定義與聯絡。

 

最小堆數組的結構體中存放頻率與對應的哈夫曼樹結點。

哈夫曼樹的葉子結點儲存字元,內結點以#儲存,並且設定父指標與一個標誌左或右孩子的標誌,為了方便從葉結點回溯列印結果。

 

構造哈夫曼樹之前,首先將葉結點放入堆中,初始化堆。 然後利用最小堆,每次取得兩個頻率最小的哈夫曼樹結點,並設最小的為父親的左孩子,次小的為父親的右孩子,然後產生一個新的結點,其頻率為兩者之和,並且修改兩個結點的父親為該結點,加入到堆中,並將兩個結點刪除。 然後再重複尋找2個最小結點,重複這樣的工作,一直運行n-1次即可全部合并完畢,這時取出堆中的最後一個結點即為哈夫曼樹的根節點。

 

為了能夠回溯列印每個葉結點的Code,我們在構造哈夫曼樹之前就應該將這些哈夫曼樹葉結點存放於一個數組中,在構造結束後,我們從數組中依次根據每個葉結點的parent指標與標誌域就可以回溯到根節點,列印出code碼。 如果我們從根節點向下遞迴列印code是無法列印完成的,思考一下就可以明白了。

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.