数据结构二叉树之Huffman编码
二叉編碼樹以及Huffman編碼
abstract:
? 在初學二叉樹的時候,我們就提出了二叉樹的一個基本應用:編碼樹。那么,就編碼樹我們究竟該如何實現(xiàn)呢?下面我們來探討這個問題。
一、PFC編碼以及解碼
前面我們講述了編碼樹的基本原理以及前綴無歧義編碼樹的要求。下面我們來考慮如何實現(xiàn)一個無前綴歧義二叉編碼樹。
我們給出一個性質:
若字符集M1和M2之間沒有公共字符,且二者構成的前綴無歧義二叉編碼樹都使用PFC編碼方案,則通過引入一個新節(jié)點合并兩個對應編碼樹的根節(jié)點T1和T2之后得到的二叉樹,就是對應于M1-M2的一種編碼方案。
二樹合并:
基于上方性質,我們不妨自底向上地構造PFC編碼樹。
規(guī)則:
首先,我們將每一個字符分別構建為一個單節(jié)點二叉樹,并將它們視作一個森林(樹的集合)。然后反復從森林中取出兩棵樹將其合二為一,顯然經過N-1次迭代以后就可以得到一棵完整的前綴無歧義二叉編碼樹。接下來,再將PFC編碼樹轉譯為編碼表。至此,對于任何待編碼文本,經過反復查閱編碼表,即可高效地將其轉化為二進制編碼。
解碼:
每當接收者從信道獲取編碼串后,接收者可以通過編碼串在編碼樹中進行漫游,即可高效地進行解碼。
過程:
若對字符A B C D E F G H進行編碼
1、初始化PFC編碼森林(森林是樹的集合,這里我們用Vector存儲樹組成森林)
將每個字符看作一個樹,顯然每個字符都放置于樹的根節(jié)點中,然后將這些樹放入一個list或vector中:
2、構造PFC編碼樹
該過程是核心:在PFC編碼森林中,隨機選取兩個生成一個新的PFC編碼樹,同時森林的規(guī)模-1
反復迭代:
.....最終可以獲取某一前綴無歧義二叉編碼樹:
3、生成PFC編碼表:
| B | 111 |
| C | 10 |
| D | 001 |
| E | 1100 |
| F | 1101 |
| G | 010 |
| H | 011 |
最后,將編碼表存入Map字典中即可。然后根據(jù)字典即可對明文進行編碼,通過編碼結合編碼樹即可進行解碼。
二、最優(yōu)編碼樹
我們在前面最終生成一個編碼樹的時候,特意給出了一個“某一”字眼。說的,對于一個字符集,實際上可以生成很多種編碼樹。既然如此,很多種編碼樹有沒有一個是效率最高的?這就是我們的最優(yōu)編碼樹問題。
高效的編碼算法生成的編碼串應該盡可能的斷。因此,不同編碼方法的效率主要體現(xiàn)于所生成的二進制編碼串的總長度,或者更準確地說,體現(xiàn)于二進制碼長與原始文本長度的比率。
我們很容易理解:某字符的編碼長度實際上就等價于該葉節(jié)點的深度。所以,各字符的平均編碼長度就是編碼樹T中各葉結點的平均深度。
因此,為導出最優(yōu)編碼樹的構造算法,我們必須更為深入地從最優(yōu)編碼樹的性質入手。
1、最優(yōu)編碼樹的性質:
2、最優(yōu)編碼二叉樹的獲取:
根據(jù)以上兩個特點,我們給出一個構造最優(yōu)二叉樹的技巧:節(jié)點位置互換
如圖:
由于A與BC兩個葉結點之間的深度之差大于1,因此我們將A與BC之父節(jié)點位置互換:
對于任意一棵前綴無歧義二叉編碼樹,我們不斷重復這個節(jié)點互換操作,直到所有的葉節(jié)點之間的深度只差不超過1,如此一來即可獲取最優(yōu)編碼樹。
3、思考:
到目前為止,我們似乎已經完成了編碼樹的構建工作,甚至我們可以實現(xiàn)一個最優(yōu)編碼樹的獲取。。。但是我們考慮到現(xiàn)實情況,事情似乎并沒有那么簡單。通常,在一個文章中,26個英文字母出現(xiàn)的頻率并不一樣,但是如果這時候我們仍然采取上方的編碼樹構造方式,也就代表了我們忽略了這一實際情況。這時候我們考慮一下問題出現(xiàn)在何處。我們在構造編碼二叉樹的時候,森林中的樹的組合的時候是一個隨機的過程,這也就意味著我們對于每一個字符出現(xiàn)的概率均視為一致,這就是問題之所在。
三、Huffman編碼樹(最優(yōu)帶權編碼樹)
既然我們找到了問題之所在,下面我們就可以進行問題的優(yōu)化:
實際上Huffman編碼樹的構造過程與普通編碼樹構造過程基本一致,當然,在Huffman編碼樹中,我們還需要對每個節(jié)點賦予權值,然后樹的迭代合并過程,只讓權值最小的兩棵樹進行合并。并且合并后權值相加,最后我們得到的那棵樹就是Huffman編碼樹。
代碼實現(xiàn):
基本TreeNode頭文件:
Huffman樹頭文件(繼承TreeNode):
#ifndef __HUFFMANTREE__ #define __HUFFMANTREE__ #include"_Tree_Node.h" #include <vector>template <typename T> class HuffmanTree : public _Tree_Node<T>{int weight = 0;//權重public://無參構造函數(shù)HuffmanTree() {}HuffmanTree(int w,const T& d):weight(w){this->setData(d);}int getWeight() const { return this->weight; }//獲取節(jié)點權重void setWeight(int w) { this->weight = w; }//設置權重 };//遞歸式先序遍歷更新孩子層次 template <typename T> void middleRand(_Tree_Node<T>* base) {//邏輯bug?但影響不大if (base != nullptr) {base->setHeight(base->getHeight() + 1);middleRand(base->getlChild());middleRand(base->getrChild());} }//合成樹函數(shù): template <typename T> HuffmanTree<T>* combain(HuffmanTree<T>& treeLeft,HuffmanTree<T>& treeRight) {HuffmanTree<T>* baseTree = new HuffmanTree<T>();baseTree->setlChild(treeLeft);baseTree->setrChild(treeRight);treeLeft.setParent(*baseTree);treeRight.setParent(*baseTree);baseTree->setTreeHeight((treeLeft.getTreeHeight() >= treeRight.getTreeHeight() ? treeLeft.getTreeHeight() : treeRight.getTreeHeight())+1);baseTree->setWeight(treeLeft.getWeight() + treeRight.getWeight());//更新子樹各節(jié)點的層次middleRand(&treeLeft);middleRand(&treeRight);return baseTree; } #endif // !__HUFFMANTREE__代碼實現(xiàn):
main函數(shù)文件:
運行結果:
00BFFC88 00BFFC60 00BFFC38 00BFFC10 00BFFBE8 00BFFBC0 00BFFB98 00BFFB70 00BFFB4800BFFB20 --------------------------當前節(jié)點地址00F4DBE0 當前節(jié)點高度:0 當前節(jié)點父親:00000000 當前節(jié)點左孩子:00F4DA00 當前節(jié)點右孩子:00F4D780當前節(jié)點數(shù)據(jù): --------------------------4打印樹的信息: --------------------------當前節(jié)點地址00F4DBE0 當前節(jié)點高度:0 當前節(jié)點父親:00000000 當前節(jié)點左孩子:00F4DA00 當前節(jié)點右孩子:00F4D780當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00F4DA00 當前節(jié)點高度:1 當前節(jié)點父親:00F4DBE0 當前節(jié)點左孩子:00F4DB90 當前節(jié)點右孩子:00F4D910當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00F4DB90 當前節(jié)點高度:2 當前節(jié)點父親:00F4DA00 當前節(jié)點左孩子:00BFFC88 當前節(jié)點右孩子:00F4D8C0當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00BFFC88 當前節(jié)點高度:3 當前節(jié)點父親:00F4DB90 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):a ----------------------------------------------------當前節(jié)點地址00F4D8C0 當前節(jié)點高度:3 當前節(jié)點父親:00F4DB90 當前節(jié)點左孩子:00BFFB48 當前節(jié)點右孩子:00BFFBC0當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00BFFB48 當前節(jié)點高度:4 當前節(jié)點父親:00F4D8C0 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):i ----------------------------------------------------當前節(jié)點地址00BFFBC0 當前節(jié)點高度:4 當前節(jié)點父親:00F4D8C0 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):f ----------------------------------------------------當前節(jié)點地址00F4D910 當前節(jié)點高度:2 當前節(jié)點父親:00F4DA00 當前節(jié)點左孩子:00BFFBE8 當前節(jié)點右孩子:00BFFC60當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00BFFBE8 當前節(jié)點高度:3 當前節(jié)點父親:00F4D910 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):e ----------------------------------------------------當前節(jié)點地址00BFFC60 當前節(jié)點高度:3 當前節(jié)點父親:00F4D910 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):b ----------------------------------------------------當前節(jié)點地址00F4D780 當前節(jié)點高度:1 當前節(jié)點父親:00F4DBE0 當前節(jié)點左孩子:00F4D870 當前節(jié)點右孩子:00F4DC30當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00F4D870 當前節(jié)點高度:2 當前節(jié)點父親:00F4D780 當前節(jié)點左孩子:00F4DB40 當前節(jié)點右孩子:00BFFB20當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00F4DB40 當前節(jié)點高度:3 當前節(jié)點父親:00F4D870 當前節(jié)點左孩子:00BFFC10 當前節(jié)點右孩子:00BFFC38當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00BFFC10 當前節(jié)點高度:4 當前節(jié)點父親:00F4DB40 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):d ----------------------------------------------------當前節(jié)點地址00BFFC38 當前節(jié)點高度:4 當前節(jié)點父親:00F4DB40 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):c ----------------------------------------------------當前節(jié)點地址00BFFB20 當前節(jié)點高度:3 當前節(jié)點父親:00F4D870 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):j ----------------------------------------------------當前節(jié)點地址00F4DC30 當前節(jié)點高度:2 當前節(jié)點父親:00F4D780 當前節(jié)點左孩子:00BFFB98 當前節(jié)點右孩子:00BFFB70當前節(jié)點數(shù)據(jù): ----------------------------------------------------當前節(jié)點地址00BFFB98 當前節(jié)點高度:3 當前節(jié)點父親:00F4DC30 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):g ----------------------------------------------------當前節(jié)點地址00BFFB70 當前節(jié)點高度:3 當前節(jié)點父親:00F4DC30 當前節(jié)點左孩子:00000000 當前節(jié)點右孩子:00000000當前節(jié)點數(shù)據(jù):h --------------------------結果圖示:
總結
以上是生活随笔為你收集整理的数据结构二叉树之Huffman编码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器安装Centos 7系统
- 下一篇: debian安装docker