用C++實現文件壓縮(1 哈弗曼編碼)


今天下午想把文件壓縮寫一下,因為我覺得這個還是比較鍛煉技術的,對數據結構的要求應該比較高,權當練習了吧。

我采用的壓縮方式是Huffman編碼,不過比較囧的是,我拼寫拼錯了,我拼的是haffman,在后面的代碼也是出盡洋相。

huffman是非常經典的一種編碼形式,不過現在好像用的不多了,但是這種壓縮編碼的好處在於數據是無損壓縮的,而且非常經典。

在構造huffman樹,要做的步驟如下:

1 對文件的元素進行統計,其實就是個計數,我這里的元素是以字節為單位的,畢竟任何文件都可以轉化為字節流的形式,處理起來方便的很。

2 然后對統計結果進行一次由小到大的排序,排序的key值就是元素出現的次數。

3 接着取出來排好序的數組的前兩個元素,求和,並且再其左右子樹上創建這兩個元素的副本,然后再扔回首部,此時扔回數組中的,就是包含子樹的節點了。在這里有個代碼優化的小技巧,能在一定程度上提高效率,減少不必要的操作。

4 此時原來已經排好序的數組可能會遭到破壞,必須重新進行排序,因為數組大體是有序的,所以使用此時效率最高的一步插入排序,時間復雜度僅為O(n),我專門寫了個函數來實現這個功能,並且進行了簡單的測試。

5 然后再取出首部的前兩個元素,重復上面步驟3,直到數組中的元素個數為1.

最終數組中僅存的元素,就是一棵二叉樹的Root。

這樣huffman樹的構造就完成了。

在huffman樹構造完成后,接着要做的就是根據huffman樹獲取其huffman編碼,這個還是很簡單的,就是使用遞歸,左樹傳入0,右樹傳入1,依次遞歸下去,遇到葉子節點就直接輸出,當然了,如果需要添加到某個數據結構中,只要在遞歸函數中給個參數接口,或者設置一個全局的容器,都能解決,換言之,類似於輸出重定向。

這樣上面的步驟完成后,就能很好的獲取到huffman編碼了。不過根據huffman編碼結果將字符型的0,1寫入到二進制中的0,1還有一些比較關鍵的函數需要處理,還沒想到比較好的點子,不過最基礎的蠻力方法肯定是能寫的,但是,效率就不好說了。

另外獲取huffman編碼中,用於0,1傳遞的參數肯定是要用一個buffer的,這個buffer我用的是char字符串,現在有個問題就是,這個字符串的長度只要大於多少就能滿足任何2GB以下文件的huffman編碼呢?

我覺得這個題可以用來出一道筆試面試題,好好想一想就能想明白的。我的結果是 254,你覺得呢?

最近筆試面試寫了不少題,都是被人考,現在我來考考別人~

另外huffman的解碼我也基本想到思路了,明天有時間的話就試着明天實現。

程序運行結果:

第一列是字符的數值,第二列是出現的次數,第三列是其huffman編碼。

我這代碼放眼望去,盡是被拼成了haffman的錯誤拼寫……

程序代碼:

  1 #include <iostream>
  2 #include <windows.h>
  3 #include <vector>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 struct HaffmanStruct
  8 {
  9     //a small structure
 10     HaffmanStruct():val(0),ncounts(0),lNext(NULL),rNext(NULL){}
 11     bool operator < (HaffmanStruct &);
 12     bool operator > (HaffmanStruct &);
 13     void Reset();
 14     unsigned char val;
 15     unsigned int ncounts;
 16     //used for tree
 17     HaffmanStruct * lNext;
 18     HaffmanStruct * rNext;
 19 };
 20 
 21 bool HaffmanStruct::operator > (HaffmanStruct & hs)
 22 {
 23     return ncounts > hs.ncounts;
 24 }
 25 
 26 bool HaffmanStruct::operator < (HaffmanStruct & hs)
 27 {
 28     return hs.ncounts > ncounts;
 29 }
 30 
 31 void HaffmanStruct::Reset()
 32 {
 33     val = 0;
 34     ncounts = 0;
 35     lNext = NULL;
 36     rNext = NULL;
 37 }
 38 
 39 /*
 40 Created in 2013 10 03
 41 ergodic the Haffman tree
 42 */
 43 template <class T>
 44 void ErgodicTree(T & Root, char * szStr, int nDeep)
 45 {
 46     if(Root.lNext == NULL && Root.rNext == NULL)
 47         cout << (int)Root.val << ends << Root.ncounts \
 48         << ends <<szStr << endl;
 49     szStr[nDeep] = '0';
 50     if(Root.lNext != NULL)
 51         ErgodicTree(*Root.lNext, szStr, nDeep + 1);
 52     szStr[nDeep] = '1';
 53     if(Root.rNext != NULL)
 54         ErgodicTree(*Root.rNext, szStr, nDeep + 1);
 55     szStr[nDeep] = 0;
 56 }
 57 
 58 /*
 59 Created in 2013 10 03
 60 Used in a Array when only one of its member is not sorted
 61 for ascend sequece
 62 */
 63 template <class T>
 64 void SingleSort(T * pHead, int nLength, const int nModifiedAddr)
 65 {
 66     if(pHead == NULL || nLength <= 0)
 67         return;
 68     int nOffSet = 0;
 69     int nFront, nBack;
 70     T tmpVal;
 71     for(int i = nModifiedAddr + 1;i < nLength;++ i)
 72     {
 73         if(pHead[nModifiedAddr] > pHead[i])
 74         {
 75             nOffSet ++;
 76         }
 77         else
 78             break;
 79     }
 80     if(nOffSet != 0)
 81     {
 82         tmpVal = pHead[nModifiedAddr];
 83         nFront = nModifiedAddr;
 84         nBack = nModifiedAddr + nOffSet;
 85         while(nFront != nBack)
 86         {
 87             pHead[nFront] = pHead[nFront + 1];
 88             nFront ++;
 89         }
 90         pHead[nBack] = tmpVal;
 91         return;
 92     }
 93 
 94     for(int i = nModifiedAddr - 1;i >= 0;-- i)
 95     {
 96         if(pHead[nModifiedAddr] < pHead[i])
 97         {
 98             nOffSet --;
 99         }
100         else
101             break;
102     }
103     if(nOffSet != 0)
104     {
105         tmpVal = pHead[nModifiedAddr];
106         nFront = nModifiedAddr + nOffSet;
107         nBack = nModifiedAddr;
108         while(nFront != nBack)
109         {
110             pHead[nBack] = pHead[nBack - 1];
111             nBack --;
112         }
113         pHead[nFront] = tmpVal;
114         return;
115     }
116 
117 }
118 
119 int _tmain(int argc, _TCHAR* argv[])
120 {
121     unsigned int aCharCountArray[256] = {0};
122     unsigned char szBuf[256];
123     DWORD dwReadNum;
124     vector<HaffmanStruct> vecValidNumberArray;
125     HANDLE hFile = CreateFile(
126         _T("C:\\record.txt"),
127         GENERIC_READ,
128         FILE_SHARE_READ,
129         0,
130         OPEN_EXISTING,
131         FILE_ATTRIBUTE_READONLY,
132         NULL);
133     if(hFile == INVALID_HANDLE_VALUE)
134         return 0;
135     while(ReadFile(hFile, szBuf, 256, &dwReadNum, NULL))
136     {
137         if(dwReadNum == 0)
138             break;
139         for(unsigned int i = 0;i < dwReadNum;++ i)
140         {
141             //words count
142             aCharCountArray[szBuf[i]] ++;
143         }
144     }
145     //used in the follow two part's code,temp value
146     HaffmanStruct ValidStruct;
147     for(int i = 0;i < 256;++ i)
148     {
149         if(aCharCountArray[i] != 0)
150         {
151             ValidStruct.val = i;
152             ValidStruct.ncounts = aCharCountArray[i];
153             vecValidNumberArray.push_back(ValidStruct);
154         }
155     }
156     sort(vecValidNumberArray.begin(), vecValidNumberArray.end());
157     while(vecValidNumberArray.size() != 1)
158     {
159         ValidStruct.Reset();
160         ValidStruct.ncounts = vecValidNumberArray[0].ncounts + vecValidNumberArray[1].ncounts;
161         ValidStruct.lNext = new HaffmanStruct;
162         *ValidStruct.lNext = vecValidNumberArray[0];
163         ValidStruct.rNext = new HaffmanStruct;
164         *ValidStruct.rNext = vecValidNumberArray[1];
165         vecValidNumberArray.erase(vecValidNumberArray.begin());
166         vecValidNumberArray[0] = ValidStruct;
167         SingleSort(&vecValidNumberArray[0], vecValidNumberArray.size(), 0);
168     }
169 
170     char Buf[256] = {0};
171     ErgodicTree(vecValidNumberArray[0], Buf, 0);
172 
173     /*
174     int vecSize = vecValidNumberArray.size();
175     for(int i = 0;i < vecSize;++ i)
176     {
177         cout << (int)vecValidNumberArray[i].val << ends << \
178         vecValidNumberArray[i].ncounts << endl;
179     }*/
180     CloseHandle(hFile);
181     system("pause");
182     return 0;
183 }

 

 


免責聲明!

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



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