算法 哈夫曼树 编码

问题来源:对一段文字怎样编码能够在保证数据正确传输的前提下数据量最小

不等长编码相比等长编码在某些情况下能够解决上述条件,如何构造符合要求的不等长编码就是哈夫曼树需要解决的问题,此不等长编码就是哈夫曼编码

 

不等长编码相比等长编码在相同条件下能够节约编码字符,但是有下面两个问题需要解决
1、使用频率高的字符编码尽可能短
2、编码不能有二义性,任何一个字符编码不能是另一个编码的前缀
哈夫曼树使用频率越高的节点距离根越近,编码越短。 每个字符都是叶子,没有孩子所以任意一个字符编码不可能是另外编码的前缀;即同一级兄弟节点的编码肯定不相同
通过上述两点哈夫曼编码巧妙的解决了不等长编码的两个约束条件

哈夫曼树创建规则
1、初始森林为带权值的根节点组成的森林
2、选出无双亲权值最小的两个节点
3、选出的两个节点为左右子树生成一个新树,父节点的权值为选出两个节点之和
4、新树加入森林,重复步骤2-4;直到森林中只有一棵树

#!/usr/bin/env python
# -*- coding:utf-8 -*-


#存储哈夫曼表信息的列表,每个元素都是Node类型
huffList = []
huffCodeList = []
init_len = 0 #huff初始化完成以后的长度
#定义每个节点的信息
class Node(object):
    def __init__(self, value, weight, parent, lchild, rchild):
        self.vaule = value
        self.weight = weight
        self.parent = parent
        self.lchild = lchild
        self.rchild = rchild

def init_append_node(value="-1", weight=-1, parent=-1, lchild=-1, rchild=-1):
    huffList.append(Node(value, weight, parent, lchild, rchild))

def init_huffList():
    while True:
        recv = input("请输入原始节点信息,值 权重以空格分开;如果全部结束请输入end")
        if (recv.strip()).lower() == "end":
            return 0
        recvList = recv.split(" ")
        value = str(recvList[0])
        weight = int(recvList[1])
        init_append_node(value=value, weight=weight)

#填写哈夫曼表主逻辑
def createHuffTable():
    lenhuffList = len(huffList)
    if lenhuffList <= 2:
        return "the tree length less than 2"
    """
    遍历列表,取出没有双亲的最小两个子树生成一个新子树,新子树加入huffList
    """
    #取出huffList中起始两个无父节点的子树作为起始节点,权重小的索引记录为temp_index1,另外一个记录为temp_index_2
    temp_index1 = None
    temp_index2 = None
    #取出前两个无双亲的节点,作为临时变量来和后面的对象进行比较,其中temp_index2权值大于temp_index1
    for i in range(lenhuffList):
        #判断本趟循环是否已经取到值了
        if (temp_index1 is not None) and (temp_index2 is not None):
            break
        #判断选择的节点是否已经被操作过
        elif huffList[i].parent != -1:
            continue
        elif temp_index1 is not None: #保证先给temp_index1先赋值,保证排序的稳定
            temp_index2 = i
        else:
            temp_index1 = i
    if huffList[temp_index2].weight < huffList[temp_index1].weight: #注意<和<=使用需要保证排序的稳定
        temp_index1, temp_index2 = temp_index2, temp_index1

    #取出权值最小且无双亲的两个节点下标,temp_index1<=temp_index2。使用两个变量取出两个最下肢
    for j in range(temp_index2, lenhuffList):
        if huffList[j].parent != -1:
            continue
        # 第二个需要大于等于,否则遗漏=最小值场景。
        elif (huffList[j].weight < huffList[temp_index2].weight) and (huffList[j].weight >= huffList[temp_index1].weight):
            temp_index2 = j
        elif huffList[j].weight < huffList[temp_index1].weight:
            temp_index1 = j
        else:
            continue
    #使用权重最小无双亲节点构造新树,新树的根加入huffList
    init_append_node(weight=(huffList[temp_index1].weight+huffList[temp_index2].weight), lchild=temp_index1, rchild=temp_index2)
    #将取出的两个权重最小值双亲信息进行修改
    huffList[temp_index1].parent = len(huffList)-1
    huffList[temp_index2].parent = len(huffList)-1



"""
哈夫曼树表生成以后就可以根据表来生成对应编码了
1、遍历表找到无child的节点,找到此节点的父节点,判断此节点是对应父节点的左孩子还是右孩子,左孩子记录为0,右孩子记录为1
2、对于父节点重复步骤1,直到根节点;使用列表记录,每次insert[0]
"""
#初始化code存储表;循环调用code需要注意调用前清空此列表
temp_index_code = []

def huffCode(index):
    if huffList[index].parent != -1:
        if huffList[huffList[index].parent].lchild == index:
            temp_index_code.insert(0, 0)
        else:
            temp_index_code.insert(0, 1)
        huffCode(huffList[index].parent)



#n个叶子节点组成的二叉树一共有2n-1个节点,循环调用,直到huffList长度为2n-1则结束调用
def createHuffTable_main():
    init_huffList()
    init_len = len(huffList)
    while len(huffList) < (2*init_len-1):
        createHuffTable()

    return huffList

if __name__ == "__main__":
    createHuffTable_main()
#    init_codeList()
    huffCode(1)
    print(temp_index_code)