哈夫曼編碼是貪心算法的一個典型應用。哈夫曼編碼利用每條數據出現的頻率(概率),從信息論的角度出發,將這些數據重新編碼。哈夫曼編碼的編碼結果是給出現頻率較高的數據一個較短的編碼,給出現頻率較低的數據一個較長的編碼。
讓我們舉個例子說明一下哈夫曼編碼的步驟:現在有數據[a,a,a,a,a,b,b,b,b,c,c,c,d,d,e]。可以看出字符a出現了5次,字符2出現了4次,字符c出現了3次,字符d出現了2次,字符e出現了1次。
首先,我們將這5個字符看成一個森林,它們的值是它們出現的頻率,我們首先要做的是把這些森林合併成一棵哈夫曼樹。利用貪心算法合併每個節點。
合併算法:先選取兩個根節點頻率值最小的森林,將這兩個森林作合併爲一棵樹,其根節點的值爲上述兩個根節點頻率值的和,上述兩個根節點爲新樹根節點的左右節點。重複合併步驟,直到將所有森林合併成一棵樹算法終止。算法過程如下圖:
步驟 | 狀態 |
---|---|
init | |
1 | |
2 | |
3 | |
4 |
構造完哈夫曼樹之後,按照遍歷路徑來獲得每個字符的編碼即可:
編碼結果如下表:
字符 | 編碼 |
---|---|
a | 00 |
b | 01 |
c | 10 |
d | 110 |
e | 111 |
附上python代碼實現:
"""
@filename [huffman.py]
@author [zxy]
@version [v1.0]
@date [2020/3/13]
"""
import queue
class Node:
def __init__(self, x, k=-1, l=None, r=None, c=''):
self.freq = x
self.key = k
self.left = l
self.right = r
self.code = c
def __lt__(self, otr):
return self.freq < otr.freq
def huffman_code(data):
freqTable={}
nodeList=[]
que=queue.PriorityQueue()
codeTable={}
# frequent label init
for n in data:
if n in freqTable:
freqTable[n]+=1
else:
freqTable[n]=1
# Huffman tree init
for k,v in freqTable.items():
nodeList.append(Node(v,k))
que.put(nodeList[-1])
# Huffman tree generate
while que.qsize()>1:
n1=que.get()
n2=que.get()
n1.code='1'
n2.code='0'
nn=Node(n1.freq+n2.freq,l=n1,r=n2);
nodeList.append(nn);
que.put(nodeList[-1])
# get Huffman code
def bl(p,codestr=[]):
codestr.append(p.code)
if p.left:
bl(p.left,codestr.copy())
bl(p.right,codestr.copy())
else:
codeTable[p.key]=''.join(codestr)
bl(nodeList[-1])
# print Huffman code result
print(str(codeTable))
return codeTable
if __name__ == '__main__':
data=[1,1,1,1,1,2,2,2,2,3,3,3,4,4,5]
huffman_code(data)