Huffman樹,又稱作最優二叉樹,是一種用於信息編碼的數據結構。其通過對比各元素的頻率/數量,自底向頂構建一顆二叉樹,頻率越低的元素越在底部。在Huffman樹中,所有元素均爲葉子節點,而中間節點和根節點用於記錄兩顆孩子節點頻次和,從而避免編碼過程中可能存在的共用前綴的問題。
Huffman樹具備如下的基本功能和屬性:
(1)構建:基於元素頻率,自底向頂構建最優二叉樹;
(2)編碼:從root開始,設定左爲0,右爲1,沿着路徑進行編碼,可以證明這種編碼形式是信息量最優的編碼。
根據Huffman樹的基本屬性,不難想到用最小堆來實現。
1. 最小堆的實現
class PriorityQuene(object):
"""
最小堆的定義
:key 加入優先隊列的對象的排序key
"""
def __init__(self, key):
self.quene = []
self.key = key
def isEmpty(self):
return len(self.quene)==0
def size(self):
return len(self.quene)
def peek(self):
return self.quene[0]
def __repr__(self):
return str([obj.__dict__.get(self.key) for obj in self.quene])
def insert(self, obj):
# key爲obj對象種用於排序的字段
self.quene.append(obj)
index = self.size() - 1
while (index - 1)//2 >= 0 and self.quene[(index - 1)//2].__dict__.get(self.key) > obj.__dict__.get(self.key):
self.quene[index], self.quene[(index - 1)//2] = self.quene[(index - 1)//2], self.quene[index]
index = (index - 1)//2
def pop(self):
if self.isEmpty():
return None
if self.size() == 1:
return self.quene.pop()
else:
item = self.quene[0]
self.quene[0] = self.quene.pop()
index = 0
while 2 * index + 1 <= self.size() - 1:
if 2 * index + 1 == self.size() - 1: # 只有左節點
if self.quene[2 * index + 1].__dict__.get(self.key) < self.quene[index].__dict__.get(self.key):
self.quene[index], self.quene[2 * index + 1] = self.quene[2 * index + 1], self.quene[index]
index = 2 * index + 1
else:
break
else:
if self.quene[2 * index + 1].__dict__.get(self.key) <= self.quene[2 * index + 2].__dict__.get(self.key) and self.quene[2 * index + 1].__dict__.get(self.key) < self.quene[index].__dict__.get(self.key):
self.quene[index], self.quene[2 * index + 1] = self.quene[2 * index + 1], self.quene[index]
index = 2 * index + 1
elif self.quene[2 * index + 2].__dict__.get(self.key) < self.quene[2 * index + 1].__dict__.get(self.key) and self.quene[2 * index + 2].__dict__.get(self.key) < self.quene[index].__dict__.get(self.key):
self.quene[index], self.quene[2 * index + 2] = self.quene[2 * index + 2], self.quene[index]
index = 2 * index + 2
else:
break
return item
2. 構建節點對象和Huffman樹
class Node(object):
"""
Huffman樹中的節點,包括:
(1)葉子節點,即huffman需要編碼的字符
(2) 中間節點:兩個葉子節點之和,或者
"""
def __init__(self, frequency, value=None):
self.frequency = frequency
self.value = value # 除了葉子節點保存字符信息,其它默認爲None,可以通過此判斷是否爲葉子節點
self.parent = None
self.leftChild = None
self.rightChild = None
def isLeftChild(self):
if not self.parent:
return False
if self.parent.leftChild == self:
return True
else:
return False
def isRightChild(self):
if not self.parent:
return False
if self.parent.rightChild == self:
return True
else:
return False
def getLeftChild(self):
return self.leftChild
def getRightChild(self):
return self.rightChild
def isLeaf(self):
return self.value is not None
class HuffmanTree(object):
def __init__(self):
self.root = None
self.nodeList = None
self.encoding_dict = {}
def buildTree(self, nodeList: list): # 將Node對象放入優先隊列中,然後自底向上構建huffman數
priQuene = PriorityQuene('frequency') # 以Node對象的frequency屬性作爲排序依據
self.nodeList = nodeList
for node in self.nodeList:
priQuene.insert(node)
while priQuene.size() >= 2:
node_left = priQuene.pop() # type: Node
node_right = priQuene.pop() # type: Node
node_parent = Node(node_left.frequency+node_right.frequency)
node_left.parent = node_parent
node_right.parent = node_parent
node_parent.leftChild = node_left
node_parent.rightChild = node_right
priQuene.insert(node_parent)
self.root = priQuene.pop()
def encoding(self):
# 爲整個huffman樹編碼
# 左子樹編碼爲0, 右子樹編碼爲1
for node in self.nodeList:
node_coding = '' # 初始化
cur = node # type: Node
while cur != self.root:
if cur.isLeftChild():
node_coding += '0'
else:
node_coding += '1'
cur = cur.parent
self.encoding_dict.update({node.value: node_coding[::-1]})
return self.encoding_dict