huffman編碼【代碼】


哈夫曼編碼應該算數據結構“樹”這一章最重要的一個問題了,當時大一下學期學的時候沒弄懂,一年后現在算是明白了。

首先,講講思路。

正好這學期在學算法,這里面就用到了貪心算法,剛好練練手。

整個問題有幾個關鍵點:

1,首先是要思考怎么樣存下從txt中讀取的所有字符中的每種字符出現的次數,首先想到的應該是結構體數組,再仔細想想不對呀,時間復雜度太高了,每次判斷一個字符都對知道它屬於結構體數組中的哪一個,那要是txt中有很多字符,那光這個就得花好多時間。然后想想,如果讀取的字符如果只有那255個ASCII中的字符時可以直接用一個整形數組來表示呀!數組的下標為整數,整數和字符數不正是通過ASCII碼一一對應了嗎。當然這些字符必須全部是那255個中的。所以整形數組的大小也只需255.當然了,如果對C++關聯容器的知識比較熟悉,用關聯容器更方便,畢竟,我們這種方法其實就是在模擬關聯容器。 

2,然后我們怎么從剛才得到的各個字符的頻率來整出一顆哈夫曼樹呢?首先,離散數學上講的構建哈夫曼樹應該是比較容易理解的,就是每次選取兩個權值(也就是這里的頻率)最小的兩個節點作為左右孩子(小的在左,大的在右),然后把它們的權值之和作為一個新的待選擇的結點去和剩余結點判斷,自底向上構建,直到剩余權值最大的一個結點,完畢。雖然這樣說着很簡單,但是落實到代碼就值得思考了。我們該怎么樣表示這樣一棵樹呢?習慣性地用指針?認真思考后我們發現,用指針行不通,因為這棵樹是自底向上構建的,我們在構建完成之前是不知道這棵樹的遍歷序列的,也就沒法通過遞歸的形式來創建這棵樹。那么,我們現在應該想到的是用數組,沒錯,用數組可以!顯然是結構體數組,所以得考慮結構體中要有哪些變量,首先要有每個結點表示的字符,對應的權值,還要有左右孩子的下標,雙親的下標。這樣就可以在數組中存下這棵樹了。

3,在得到哈夫曼樹后,該怎么輸出每個字符對應的哈夫曼編碼呢?從每個葉子結點出發,逐步向根結點方向遍歷,每次到達其雙親時,判斷自己是雙親的左孩子還是右孩子,如果是左孩子,在該葉子表示的字符對應的編碼(用string表示)加上0,否則加上1。然后再找到雙親的雙親,。。。直到到達根結點(根結點沒有雙親,對應的雙親下標可以設為0),由於這樣得到的編碼01字符串是反的,所以我們要反一下就得到了正確的編碼。

 

這里放一個ppt,是我們老師上課講的,便於理解 http://files.cnblogs.com/files/journal-of-xjx/huffman.pptx

注意我是把一個txt文件中的所有字符讀到一個string中去的。自己測試的時候隨便放一個input.txt進去就可以了(注意必須是ASCII小於等於255的字符,超過這個范圍的字符不能用這種方式表示)

代碼如下:

  1 #include <iostream>
  2 #include <fstream>
  3 #include <algorithm>
  4 
  5 using namespace std;
  6 
  7 #define NUM_CHARS 256   //讀取的字符串中最多出現這么多種字符
  8 #define MAX_FREQ  10000   //最大頻率必須小於這個數
  9 #define MAX_SIZE  512
 10 
 11 //Huffman Tree結點
 12 typedef struct HuffNode
 13 {
 14     char data;
 15     unsigned int freq;
 16     int parent;
 17     int lchild;
 18     int rchild;
 19 }HuffNode;
 20 //編碼結點
 21 typedef struct HuffCode
 22 {
 23     char data;//保存字符
 24     string s;//保存字符對應的編碼
 25 }HuffCode;
 26 
 27 //給定一個字符串,把字符的出現頻率保存到freqs數組中
 28 //注意字符出現的頻率不能超出unsigned int所能表示的范圍
 29 int Create_freq_array(unsigned int (&freqs)[NUM_CHARS],string s, int &char_size)//傳入數組的引用,
 30 {
 31     int i, maxfreq = 0;
 32     for(int i=0;i<NUM_CHARS;i++)
 33         freqs[i] = 0;//注意傳入的數組的各元素先賦值為0
 34     for(auto iter =s.begin(); iter!=s.end(); iter++)
 35     {
 36         freqs[*iter]++;   //*iter為char型,這里轉換成了int型,即以某個字符的ASCII碼作為
 37         if(freqs[*iter] > maxfreq)//它在freq數組中的下標,注意這種方式不能表示非ASCII碼字符!
 38             maxfreq = freqs[*iter];//每次記得更新maxfreq的值
 39     }
 40     for(i=0; i<NUM_CHARS; i++)//計算char_size值
 41     {
 42         if(freqs[i])
 43         {
 44             char_size++;
 45         }
 46     }
 47     return 0;
 48 }
 49 
 50 //打印字符頻率表
 51 int Print_freqs(unsigned int (&freqs)[NUM_CHARS],int n)
 52 {
 53     int i;
 54     char c;
 55     for(i = 0; i < NUM_CHARS; i++)
 56     {
 57         if(freqs[i])
 58         {
 59             c = i;//把i以ASCII碼值還原出對應的字符
 60             cout << "字符 " << c << " 出現的頻率為:" << freqs[i] << endl;
 61         }
 62 
 63     }
 64     cout << endl << "以上共出現" << n << "種字符" << endl <<endl;
 65     return 0;
 66 }
 67 
 68 int Build_Huffman_tree(unsigned int (&freqs)[NUM_CHARS],HuffNode (&Huffman_array)[MAX_SIZE],int n)
 69 {                                                          //n表示freqs數組中實際包含的字符種類數
 70     char c;
 71     int k = 0,x1,x2;
 72     unsigned int m1, m2;
 73 
 74     for(int i=0;i<NUM_CHARS;i++)//把前n個葉結點的信息輸入Huffman_array數組
 75     {
 76         if(freqs[i])
 77         {
 78             c=i;//還原字符
 79             Huffman_array[k].data = c;
 80             Huffman_array[k].freq = freqs[i];
 81             Huffman_array[k].parent = 0;
 82             Huffman_array[k].lchild = 0;
 83             Huffman_array[k].rchild = 0;
 84             k++;
 85         }
 86     }
 87     for(int i=n;i<2*n-1;i++)//處理剩下n-1個非葉子結點
 88     {
 89         Huffman_array[i].data = '#';
 90         Huffman_array[i].freq = 0;
 91         Huffman_array[i].parent = 0;
 92         Huffman_array[i].lchild = 0;
 93         Huffman_array[i].rchild = 0;
 94     }
 95     // 循環構造 Huffman 樹
 96     for(int i=0; i<n-1; i++)
 97     {
 98         m1=m2=MAX_FREQ; // m1、m2中存放兩個無父結點且結點權值最小的兩個結點
 99         x1=x2=0;  //x1、x2:構造哈夫曼樹不同過程中兩個最小權值結點在數組中的序號
100         /* 找出所有結點中權值最小、無父結點的兩個結點,並合並之為一顆二叉樹 */
101         for (int j=0; j<n+i; j++)
102         {
103             if (Huffman_array[j].freq < m1 && Huffman_array[j].parent==0)
104             {            //如果當前判斷的結點的權值小於最小的m1,則把它賦給m1,同時
105                 m2=m1;   //更新m1結點的下標, 保持m1是當前所有判斷過的元素中是最小的
106                 x2=x1;   //再把m1的信息賦給m2,保持m2是當前所有判斷過的元素中是第二小的
107                 m1=Huffman_array[j].freq ;
108                 x1=j;
109             }
110             else if (Huffman_array[j].freq  < m2 &&Huffman_array[j].parent==0)
111                          //如果當前判斷的結點的權值大於等於最小的m1,但是小於m2,
112             {            //則只需把它賦給m2,更新m2,保持m2是當前所有判斷過的元素中是第二小的
113                 m2=Huffman_array[j].freq ;
114                 x2=j;
115             }
116         }
117         /* 設置找到的兩個子結點 x1、x2 的父結點信息 */
118         Huffman_array[x1].parent  = n+i;
119         Huffman_array[x2].parent  = n+i;
120         Huffman_array[n+i].freq  = Huffman_array[x1].freq  + Huffman_array[x2].freq ;
121         Huffman_array[n+i].lchild = x1;
122         Huffman_array[n+i].rchild = x2;
123     }
124     return 0;
125 }
126 //哈夫曼編碼,輸出string中各種字符對應的編碼
127 int Huffman_code(HuffNode(&Huffman_array)[MAX_SIZE],HuffCode (&Huffman_code_array)[NUM_CHARS],int n)
128 {
129     int temp;
130     for(int i = 0;i < n;i++)
131     {
132         temp = i;//當前處理的Huffman_array數組下標
133         Huffman_code_array[i].data = Huffman_array[i].data;
134         while(Huffman_array[temp].parent)
135         {
136             if(Huffman_array[Huffman_array[temp].parent].lchild == temp)//左孩子為0
137             {
138                  Huffman_code_array[i].s += '0';
139             }
140             else//右孩子為1
141             {
142                 Huffman_code_array[i].s += '1';
143             }
144             temp = Huffman_array[temp].parent;
145         }
146         reverse(Huffman_code_array[i].s.begin(), Huffman_code_array[i].s.end());
147     }   //注意翻轉每一個string,這里用到了#include <algorithm>
148     return 0;
149 }
150 
151 int Print_huffman_code(HuffCode (&Huffman_code_array)[NUM_CHARS],int n)
152 {
153     for(int i = 0;i < n;i++)
154     {
155         cout << "字符 " << Huffman_code_array[i].data << " 對應的哈夫曼編碼為:" << Huffman_code_array[i].s << endl;
156     }
157     cout << endl;
158     return 0;
159 }
160 
161 int main()
162 {
163     ifstream in("input.txt",ios::in);//從input.txt中讀取輸入數據
164     ofstream out("output.txt",ios::out);//向output.txt中寫入數據
165     string s,temp;
166     int char_size = 0;//用以保存string中所包含的字符種類
167     unsigned int freqs[NUM_CHARS];
168     HuffNode Huffman_array[MAX_SIZE];
169     HuffCode Huffman_code_array[NUM_CHARS];
170     while(getline(in,temp))//按行讀取一個txt文件中的各個字符到一個string,每讀完一行加上一個'\n'
171     {
172         s += temp;
173         s += '\n';
174     }
175     cout << "輸入的字符總數為: " << s.size() << endl << endl << "其中:" << endl;//string中包含的字符數
176     Create_freq_array(freqs,s,char_size);
177     Print_freqs(freqs,char_size);
178     Build_Huffman_tree(freqs,Huffman_array,char_size);
179     Huffman_code(Huffman_array,Huffman_code_array,char_size);
180     Print_huffman_code(Huffman_code_array,char_size);
181     return 0;
182 }

 

參考了別人的一些代碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM