数据结构课程作业-哈夫曼树

设需要编码的字符集为{d1,d2,..., dn}它们出现的频率为{w1,w2,...,wn},应用哈夫曼树构造最短的不等长编码方案。

实验目的
(1)了解前缀编码的概念,理解数据压缩的基本方法
(2) 掌握最优子结构性质的证明方法
(3) 掌握贪心法的设计思想并能熟练运用

3.实验要求
(1) 证明哈夫曼树满足最优子结构性质;

(2) 设计贪心算法求解哈夫曼编码方案

(3) 设计测试数据,写出程序文档。

实验项目——哈夫曼编码

 

题目复述:

设需要编码的字符集为{d1,d,2..., dn}它们出现的频率为{w1,w2,...,wn},应用哈夫曼树构造最短的不等长编码方案。

  • 证明哈夫曼树满足最优子结构性质

首先,对于哈夫曼树的定义和性质:

1.夫曼树是一种具有最小带权路径长度的树。

2.哈夫曼树是一种最优前缀编码树,即字符出现频率越高的字符离根节点越近,从而可以用较短的编码表示高频字符,实现数据的压缩。

假设存在一个子树S不是哈夫曼树。考虑S的叶子节点集合P(P为S的所有叶子节点)和叶子节点集合Q(Q为整个哈夫曼树的所有叶子节点)。根据哈夫曼树的性质,P中的节点离根节点的路径长度必定小于等于Q中的节点离根节点的路径长度。如果将S替换为Q中路径长度最长的子树,记为S'。但由于P中的节点离根节点的路径长度小于等于Q中的节点离根节点的路径长度,所以S'的路径长度必定大于S。

然而,由于哈夫曼树是一种具有最小带权路径长度的树,S'作为哈夫曼树的子树必须满足最小带权路径长度的条件。这意味着S'的路径长度必定小于等于S。所以假设导致了矛盾,因此任意一个子树也是哈夫曼树,即哈夫曼树满足最优子结构性质,证明完毕

  • 设计贪心算法求解哈夫曼编码方案
  1. 树结构的定义

class Node:
   
#树结构定义
   
def __init__(self, char, weight):
       
self.char = char  # 字符
       
self.weight = weight  # 权重
       
self.left = None  # 左子节点
       
self.right = None  # 右子节点

2.构建哈夫曼树

给定节点名称和节点权重(在编码中可理解为信号频率),通过贪心算法(每次找到当前环境中最小的两个点)构建哈夫曼树并返回。

def begain(chars,weights):

    #根据贪心思想,返回哈夫曼树

    # chars = ['d1', 'd2', 'd3', 'd4']

    # weights = [2, 6, 8, 4]

    nodes = []

    for i in range(len(chars)):

        nodes.append(Node(chars[i], weights[i]))



    while len(nodes) > 1:

        # 按权重排序

        nodes = sorted(nodes, key=lambda x: x.weight)

        # 取最小的两个节点

        left_node = nodes.pop(0)

        right_node = nodes.pop(0)

        # 创建新节点作为它们的父节点

        parent_node = Node(None, left_node.weight + right_node.weight)

        parent_node.left = left_node

        parent_node.right = right_node

        # 将新节点加入节点列表

        nodes.append(parent_node)

    root = nodes[0# 哈夫曼树的根节点

    return root

3.根据哈夫曼树得到哈夫曼编码

code_table = {}
def generate_huffman_code(node, code):

    #根据哈夫曼树得到哈夫曼编码

    if node.char is not None:

        code_table[node.char] = code

    else:

        generate_huffman_code(node.left, code + '0')

        generate_huffman_code(node.right, code + '1')
 

4.简单测试

  • 设计测试数据,写出程序文档。
  1. 设计一个函数返回随机选的点和对应权重。这里我设置了最多12个叶子简单的选择。随机选择1-100的权重,同时有百分之20的几率会使得两个点的权重相同。具体如下:
def retruninstant():

    import numpy as np

    nodes_name = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'M']

    chose_nodes = np.random.choice(nodes_name,size=np.random.choice([5,len(nodes_name)],size=1),replace=False)

    chose_nodes_weight=[]

    for i in chose_nodes:

        #为每一个点初始化一个权重

        if np.random.choice(range(1,11),size=1) in [1,2]:#百分之30的几率选一个重复的权重

            if len(chose_nodes_weight)!=0:

                chose_nodes_weight.append(np.random.choice(chose_nodes_weight,size=1)[0])

            else:

                chose_nodes_weight.append(np.random.choice(range(1,101),size=1)[0])

        else:

            chose_nodes_weight.append(np.random.choice(range(1, 101), size=1)[0])

    print(chose_nodes,chose_nodes_weight)

    return chose_nodes,chose_nodes_weight

返回类型为:

2.测试案例使用上述程序生成的10个测试案例:

tests=[]

for i in range(10):

    test_node,tests_weight=retruninstant()

    tests.append([test_node,tests_weight])

得到测试集合为:

3.使用测试集,结合上述程序,得到结果:

for j in tests:

    code_table = {}

    #给定节点名称,和节点权重

    chars,weights = j[0],j[1]

    generate_huffman_code(begain(chars,weights), '')

    # 打印编码结果

    print(code_table)

结果:

{'C': '00', 'B': '01', 'D': '10000', 'M': '10001', 'G': '1001', 'H': '101', 'I': '110', 'J': '1110', 'K': '11110', 'A': '111110', 'E': '1111110', 'F': '1111111'}

{'E': '00', 'H': '01', 'I': '10', 'M': '110', 'C': '111'}

{'K': '00', 'M': '01', 'C': '10', 'B': '110', 'H': '111'}

{'D': '000', 'M': '001', 'F': '0100', 'B': '0101', 'A': '011', 'K': '100', 'I': '101', 'J': '110', 'H': '111000', 'E': '111001', 'C': '11101', 'G': '1111'}

{'A': '000', 'K': '001', 'D': '010', 'H': '011', 'I': '100', 'J': '1010', 'F': '1011', 'M': '1100', 'C': '1101', 'E': '1110', 'G': '11110', 'B': '11111'}

{'K': '000', 'G': '0010', 'H': '0011', 'D': '01', 'B': '1'}

{'J': '00', 'C': '010', 'H': '011', 'K': '10', 'I': '11'}

{'H': '000', 'J': '001', 'C': '010', 'K': '011', 'B': '100', 'E': '10100', 'I': '10101', 'M': '1011', 'G': '1100', 'A': '1101', 'D': '1110', 'F': '1111'}

{'H': '000', 'E': '001', 'J': '010', 'D': '011', 'F': '100', 'B': '10100', 'M': '10101', 'A': '1011', 'I': '1100', 'K': '1101', 'G': '1110', 'C': '1111'}

{'F': '000', 'E': '001', 'C': '010', 'A': '0110000', 'M': '0110001', 'G': '011001', 'J': '011010', 'H': '011011', 'D': '0111', 'K': '10', 'I': '110', 'B': '111'}

完整代码:

class Node:
    #树结构定义
    def __init__(self, char, weight):
        self.char = char  # 字符
        self.weight = weight  # 权重
        self.left = None  # 左子节点
        self.right = None  # 右子节点

def generate_huffman_code(node, code):
    #根据哈夫曼树得到哈夫曼编码
    if node.char is not None:
        code_table[node.char] = code
    else:
        generate_huffman_code(node.left, code + '0')
        generate_huffman_code(node.right, code + '1')

def begain(chars,weights):
    #根据贪心思想,返回哈夫曼树
    # chars = ['d1', 'd2', 'd3', 'd4']
    # weights = [2, 6, 8, 4]
    nodes = []
    for i in range(len(chars)):
        nodes.append(Node(chars[i], weights[i]))

    while len(nodes) > 1:
        # 按权重排序
        nodes = sorted(nodes, key=lambda x: x.weight)
        # 取最小的两个节点
        left_node = nodes.pop(0)
        right_node = nodes.pop(0)
        # 创建新节点作为它们的父节点
        parent_node = Node(None, left_node.weight + right_node.weight)
        parent_node.left = left_node
        parent_node.right = right_node
        # 将新节点加入节点列表
        nodes.append(parent_node)
    root = nodes[0]  # 哈夫曼树的根节点
    return root

def retruninstant():
    import numpy as np
    nodes_name = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'M']
    chose_nodes = np.random.choice(nodes_name,size=np.random.choice([5,len(nodes_name)],size=1),replace=False)
    chose_nodes_weight=[]
    for i in chose_nodes:
        #为每一个点初始化一个权重
        if np.random.choice(range(1,11),size=1) in [1,2]:#百分之30的几率选一个重复的权重
            if len(chose_nodes_weight)!=0:
                chose_nodes_weight.append(np.random.choice(chose_nodes_weight,size=1)[0])
            else:
                chose_nodes_weight.append(np.random.choice(range(1,101),size=1)[0])
        else:
            chose_nodes_weight.append(np.random.choice(range(1, 101), size=1)[0])
    print(chose_nodes,chose_nodes_weight)
    return chose_nodes,chose_nodes_weight


tests=[]
for i in range(10):
    test_node,tests_weight=retruninstant()
    tests.append([test_node,tests_weight])

for j in tests:
    code_table = {}
    #给定节点名称,和节点权重
    chars,weights = j[0],j[1]
    generate_huffman_code(begain(chars,weights), '')
    # 打印编码结果
    print(code_table)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值