今天下午想把文件壓縮寫一下,因為我覺得這個還是比較鍛煉技術的,對數據結構的要求應該比較高,權當練習了吧。
我采用的壓縮方式是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 }