from matplotlib import pyplot as plt
import hashlib
class HTNode:
def __init__(self, value=None, weight=0, parent=None, left_child=None, right_child=None):
self.value = value
self.weight = weight
self.parent = parent
self.left_child = left_child
self.right_child = right_child
class ShowHuffmanBiTree(object):
"""显示哈夫曼树"""
def __init__(self, root, d_hor=4, d_vec=8, radius=1.5, figsize =(11, 9)):
"""
对所展示二叉树的一些元素参数的设置
:param d_hor: 节点与节点的水平距离
:param d_vec: 节点与节点的垂直距离
:param radius: 节点的半径
:param radius: 画布大小,用一个元祖表示画布width和high,单位为inch
"""
self.root = None
self.d_hor = d_hor
self.d_vec = d_vec
self.radius = radius
self.figsize = figsize
self.root = root
def get_left_width(self, root):
"""获得根左边宽度,也是根的左子孙节点数"""
return self.get_width(root.left_child)
def get_right_width(self, root):
"""获得根右边宽度,也是根的右子孙节点数"""
return self.get_width(root.right_child)
def get_width(self, root):
"""获得树的宽度,也是该树的节点数。使用的是中序遍历方式"""
if root:
return self.get_width(root.left_child) + 1 + self.get_width(root.right_child)
else:
return 0
def get_height(self, root):
"""获得二叉树的高度, 使用后序遍历"""
if root:
return max(self.get_height(root.left_child), self.get_height(root.right_child)) + 1
else:
return 0
def get_w_h(self, root):
"""获得树的宽度和高度"""
w = self.get_width(root)
h = self.get_height(root)
return w, h
def __draw_a_node(self, x, y, value, ax):
"""画一个节点"""
c_node = plt.Circle((x, y), radius=self.radius, color="#65DDFF")
ax.add_patch(c_node)
plt.text(x, y, value, ha='center', va='center', fontsize=25, )
def __draw_a_edge(self, x1, y1, x2, y2):
"""画一条边"""
x = (x1, x2)
y = (y1, y2)
plt.plot(x, y, 'g-')
def __create_win(self, root):
"""创建窗口"""
WEIGHT, HEIGHT = self.get_w_h(root)
WEIGHT = (WEIGHT+1)*self.d_hor
HEIGHT = (HEIGHT+1)*self.d_vec
fig = plt.figure(figsize=self.figsize)
ax = fig.add_subplot(111)
plt.xlim(0, WEIGHT)
plt.ylim(0, HEIGHT)
x = (self.get_left_width(root) + 1) * self.d_hor
y = HEIGHT - self.d_vec
return fig, ax, x, y
def __print_tree_by_preorder(self, root, x, y, ax):
"""通过先序遍历打印二叉树"""
if not root:
return
self.__draw_a_node(x, y, root.value, ax)
lx = rx = 0
ly = ry = y - self.d_vec
if root.left_child:
lx = x - self.d_hor * (self.get_right_width(root.left_child) + 1)
self.__draw_a_edge(x, y, lx, ly)
if root.right_child:
rx = x + self.d_hor * (self.get_left_width(root.right_child) + 1)
self.__draw_a_edge(x, y, rx, ry)
self.__print_tree_by_preorder(root.left_child, lx, ly, ax)
self.__print_tree_by_preorder(root.right_child, rx, ry, ax)
def show_BiTree_1(self):
"""可视化二叉树"""
_, ax, x, y = self.__create_win(self.root)
self.__print_tree_by_preorder(self.root, x, y, ax)
plt.show()
def md5(a):
md5 = hashlib.md5()
md5.update(a.encode("utf-8"))
return md5.hexdigest()[:5]
class HuffmanTree:
def __init__(self):
self.leaf_nodes = []
self.root = None
def creat_HuffmanTree(self, elements_seq):
"""
创建哈夫曼树:由n个带权叶子结点构成的所有二叉树种带权路径长度最短的二叉树。
算法思想:
(1) 用给定的n个权值{w1,w2,...,wn}构造n棵二叉树并构造成的森林F={T1,T2,...,Tn},其中每一棵树
Ti(1<=i<=n)都只有一个权值为wi的根节点,其左右子树均为空。
(2) 找最小树;在森林F中选择两颗根结点权值最小的二叉树,作为一棵新二叉树的左右子树,标记新二
叉树的根结点权值为其左右子树的根结点权值之和。
(3) 从F中删除被选中的那两颗二叉树,同时把新构成的二叉树加入到森林F中
(4) 判断:重复(2)(3)操作,直到森林中只有一颗二叉树为止,此时得到的这颗二叉树就是哈夫曼树。
:param elements_seq:
:return:
"""
while elements_seq:
element = elements_seq.pop()
leaf_node = HTNode(value=element[0], weight=element[1])
self.leaf_nodes.append(leaf_node)
key = lambda node: node.weight
temp_nodes = self.leaf_nodes.copy()
while len(temp_nodes) > 1:
temp_nodes.sort(key=key, reverse=True)
left_child = temp_nodes.pop()
right_child = temp_nodes.pop()
parent_weight = right_child.weight + left_child.weight
id_ = md5(f'{left_child.value}{right_child.value}')
parent = HTNode(value=id_, weight=parent_weight, left_child=left_child, right_child=right_child)
left_child.parent = parent
right_child.parent = parent
temp_nodes.append(parent)
self.root = temp_nodes.pop()
def huffman_code(self):
"""
哈夫曼编码是前缀编码。前缀编码中的任一编码都不是其它编码的前缀(左字串),避免了非前缀编码中分隔符的使用
哈夫曼编码是最优前缀编码。对于使用频度最高的字符其编码长度最短,而频度最低的字符其编码长度最长。因此对
于n个字符,分别以它们的使用频度作为叶子全职来构造哈夫曼树,则该树对应的哈夫曼编码能使各种报文(由这n种字
符构成的文本)对应的二进制串的平均长度最短。
哈夫曼编码:按规定向左的分支标记为1,向右的分支标记为0
:return: 哈夫曼编码集
"""
code_set = {}
for leaf in self.leaf_nodes:
code = []
q = leaf
while q.parent:
if q.parent.left_child is q:
code.append('1')
else:
code.append('0')
q = q.parent
code = ''.join(code)
code_set[leaf.value] = code
return code_set
from HuffmanTree1.HuffmanTree import HuffmanTree, ShowHuffmanBiTree
if __name__ == '__main__':
param = [('c', 2), ('s', 3), ('e', 5), ('a', 7), ('t', 8)]
huffman_tree = HuffmanTree()
huffman_tree.creat_HuffmanTree(param)
ShowHuffmanBiTree(huffman_tree.root).show_BiTree_1()
print(huffman_tree.huffman_code())
"""
运行结果:
{'t': '00', 'a': '10', 'e': '01', 's': '011', 'c': '111'}
Process finished with exit code 0
"""