哈夫曼樹(shù)原理
秉著能不寫(xiě)就不寫(xiě)的理念,關(guān)于哈夫曼樹(shù)的原理及其構(gòu)建,還是貼一篇博客吧。
https://www.jb51.net/article/97396.htm
其大概流程
哈夫曼編碼代碼
# 樹(shù)節(jié)點(diǎn)類(lèi)構(gòu)建 class TreeNode(object): def __init__(self, data): self.val = data[0] self.priority = data[1] self.leftChild = None self.rightChild = None self.code = "" # 創(chuàng)建樹(shù)節(jié)點(diǎn)隊(duì)列函數(shù) def creatnodeQ(codes): q = [] for code in codes: q.append(TreeNode(code)) return q # 為隊(duì)列添加節(jié)點(diǎn)元素,并保證優(yōu)先度從大到小排列 def addQ(queue, nodeNew): if len(queue) == 0: return [nodeNew] for i in range(len(queue)): if queue[i].priority >= nodeNew.priority: return queue[:i] + [nodeNew] + queue[i:] return queue + [nodeNew] # 節(jié)點(diǎn)隊(duì)列類(lèi)定義 class nodeQeuen(object): def __init__(self, code): self.que = creatnodeQ(code) self.size = len(self.que) def addNode(self,node): self.que = addQ(self.que, node) self.size += 1 def popNode(self): self.size -= 1 return self.que.pop(0) # 各個(gè)字符在字符串中出現(xiàn)的次數(shù),即計(jì)算優(yōu)先度 def freChar(string): d ={} for c in string: if not c in d: d[c] = 1 else: d[c] += 1 return sorted(d.items(),key=lambda x:x[1]) # 創(chuàng)建哈夫曼樹(shù) def creatHuffmanTree(nodeQ): while nodeQ.size != 1: node1 = nodeQ.popNode() node2 = nodeQ.popNode() r = TreeNode([None, node1.priority+node2.priority]) r.leftChild = node1 r.rightChild = node2 nodeQ.addNode(r) return nodeQ.popNode() codeDic1 = {} codeDic2 = {} # 由哈夫曼樹(shù)得到哈夫曼編碼表 def HuffmanCodeDic(head, x): global codeDic, codeList if head: HuffmanCodeDic(head.leftChild, x+'0') head.code += x if head.val: codeDic2[head.code] = head.val codeDic1[head.val] = head.code HuffmanCodeDic(head.rightChild, x+'1') # 字符串編碼 def TransEncode(string): global codeDic1 transcode = "" for c in string: transcode += codeDic1[c] return transcode # 字符串解碼 def TransDecode(StringCode): global codeDic2 code = "" ans = "" for ch in StringCode: code += ch if code in codeDic2: ans += codeDic2[code] code = "" return ans # 舉例 string = "AAGGDCCCDDDGFBBBFFGGDDDDGGGEFFDDCCCCDDFGAAA" t = nodeQeuen(freChar(string)) tree = creatHuffmanTree(t) HuffmanCodeDic(tree, '') print(codeDic1,codeDic2) a = TransEncode(string) print(a) aa = TransDecode(a) print(aa) print(string == aa)
接下來(lái)就是一段一段分析代碼
1.樹(shù)結(jié)點(diǎn)類(lèi)的構(gòu)建:
共有5個(gè)屬性:結(jié)點(diǎn)的值,結(jié)點(diǎn)的優(yōu)先度,結(jié)點(diǎn)的左子結(jié)點(diǎn),結(jié)點(diǎn)的右子結(jié)點(diǎn),結(jié)點(diǎn)值的編碼(這個(gè)沒(méi)有什么好說(shuō)的,這些屬性都是被需要的)
2.創(chuàng)建樹(shù)結(jié)點(diǎn)隊(duì)列函數(shù):
對(duì)于所有的字母結(jié)點(diǎn),我們將其組成一個(gè)隊(duì)列,這里使用list列表來(lái)完成隊(duì)列的功能。將所有樹(shù)節(jié)點(diǎn)夠放進(jìn)列表中,當(dāng)然傳進(jìn)來(lái)的是按優(yōu)先度從小到大已排序的元素列表
3.為隊(duì)列添加節(jié)點(diǎn)元素,并保證優(yōu)先度從大到小排列:
當(dāng)有新生成的結(jié)點(diǎn)時(shí),需將其插入列表,并放在合適位置,使隊(duì)列依然時(shí)按優(yōu)先度從小打到排列的。
4.結(jié)點(diǎn)隊(duì)列類(lèi)定義:
創(chuàng)建類(lèi)初始化時(shí)需要傳進(jìn)去的是一個(gè)列表,列表中的每個(gè)元素是由字母與優(yōu)先度組成的元組。元組第一個(gè)元素是字母,第二個(gè)元素是優(yōu)先度(即在文本中出現(xiàn)的次數(shù))
類(lèi)初始化化時(shí),調(diào)用“創(chuàng)建樹(shù)結(jié)點(diǎn)隊(duì)列函數(shù)”,隊(duì)列中的每個(gè)元素都是一個(gè)樹(shù)結(jié)點(diǎn)。
類(lèi)中還包含一個(gè)隊(duì)列規(guī)模屬性以及另外兩個(gè)操作函數(shù):添加結(jié)點(diǎn)函數(shù)和彈出結(jié)點(diǎn)函數(shù)。
添加結(jié)點(diǎn)函數(shù)直接調(diào)用之前定義的函數(shù)即可,輸入的參數(shù)為隊(duì)列和新結(jié)點(diǎn),并且隊(duì)列規(guī)模加一
彈出第一個(gè)元素則直接調(diào)用列表的pop(0)函數(shù),同時(shí)隊(duì)列規(guī)模減一
5.計(jì)算文本中個(gè)字母的優(yōu)先度,即出現(xiàn)的次數(shù):
定義一個(gè)字典,遍歷文本中的每一個(gè)字母,若字母不在字典里說(shuō)明是第一次出現(xiàn),則定義該字母為鍵,另鍵值為1,若在字典里有,則只需將相應(yīng)的鍵值加一。 遍歷后就得到了每個(gè)字母出現(xiàn)的次數(shù)。
6.由哈夫曼樹(shù)得到編碼表:
這里定義了兩個(gè)全局字典,用于存放字母編碼,一個(gè)字典用于編碼,另一個(gè)字典用于解碼,這樣程序操作起來(lái)比較方便。
這里主要就是遍歷,運(yùn)用的是二叉樹(shù)的中序遍歷。如果明白中序遍歷的化,就能看懂這里的代碼,每遞歸到深一層的時(shí)候,就在后面多加一個(gè)‘0'(左子樹(shù))或‘1'(右子樹(shù))。
中序遍歷我在上一篇博客中講的還算可以吧,不懂的可以參考一下,否則就可以略過(guò)這一段。
這一段是哈夫曼編碼的關(guān)鍵,也是難點(diǎn),希望能夠好好理解一下,也是對(duì)遞歸的一個(gè)理解。這一點(diǎn)沒(méi)問(wèn)題的話,我覺(jué)得哈夫曼樹(shù)真的挺簡(jiǎn)單的!!!
7.字符串編碼,字符串解碼:
這兩段我就不詳細(xì)說(shuō)了,應(yīng)為已經(jīng)有編碼與解碼的字典了,所以對(duì)應(yīng)每一個(gè)字母直接在字典里找就好了,而且字典的尋找速度還是相當(dāng)快的。
差不多了,例子就不舉了,確實(shí)哈夫曼樹(shù)比之前的什么八皇后問(wèn)題還有KMP問(wèn)題簡(jiǎn)單多了。
最后向Huffman大神致敬,祝各位學(xué)有所成。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
