统计和生成所有不同的二叉树

统计和生成所有不同的二叉树

题目

给定一个整数n,如果n<1,代表空树,否则代表中序遍历的结果为1,2,3…n,求可能的二叉树结构有多少。例如:
n=-1表示空树,返回1
n=2,中序遍历的1,2,满足的二叉树有:

1
null
2
null
null
1
null
2
null
null

思路

假设f(n)表示中序遍历序列为1,2…n可表示的二叉树数目,该树一定是二叉搜索树。
则1,2,…n都可以表示根节点,假设1<=a<=n,a为根的二叉搜索树种数: f(a-1) * f(n-a)
则f(n) = f(i-1)*f(n-a), for i in [1,n]

实现1,递归

@timethis
def num_bst1(n):
    def recursive(n):
        if n < 2:
            return 1

        num = 0
        for i in range(1, n+1):
            num += recursive(i-1) * recursive(n-i)

        return num

    return recursive(n)

包含大量重复计算,可以预计这个实现效率不会太高

实现2,带记忆的递归

@timethis
def num_bst2(n):
    def recursive(n):
        if n in d:
            return d[n]

        if n < 2:
            d[n] = 1
            return 1

        num = 0
        for i in range(1, n+1):
            num += recursive(i-1) * recursive(n-i)

        d[n] = num
        return num

    d = {}
    return recursive(n)

实现3,动态规划

计算f(n)依赖f(1), f(2) … f(i),这种是动态规划发挥作用的好地方:

@timethis
def num_bst(n):
    if n < 2:
        return 1

    dp = [0 for i in range(n+1)]
    dp[0] = 1

    for i in range(1, n+1):
        for j in range(1, i+1):
            dp[i] += dp[j-1] * dp[i-j]

    return dp[n]

测试

import time
from functools import wraps


class TreeNode():
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None


def timethis(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, 'cost time:', end - start)
        return result
    return wrapper

def test(n):
    n0 = num_bst(n)
    n1 = num_bst1(n)
    n2 = num_bst2(n)
    print(n0, n1, n2)

if __name__ == '__main__':
    test(3)
    test(8)
    test(13)
    test(33)

结果

num_bst cost time: 6.4373016357421875e-06
num_bst1 cost time: 5.0067901611328125e-06
num_bst2 cost time: 5.0067901611328125e-06
5 5 5
num_bst cost time: 9.5367431640625e-06
num_bst1 cost time: 0.0005745887756347656
num_bst2 cost time: 1.3589859008789062e-05
1430 1430 1430
num_bst cost time: 1.5735626220703125e-05
num_bst1 cost time: 0.16288375854492188
num_bst2 cost time: 4.2438507080078125e-05
742900 742900 742900
num_bst cost time: 0.0001049041748046875
^CTraceback (most recent call last):
  File "count_and_generate_bst.py", line 147, in <module>
    test(33)
  File "count_and_generate_bst.py", line 132, in test
    n1 = num_bst1(n)
  File "count_and_generate_bst.py", line 16, in wrapper
    result = func(*args, **kwargs)
  File "count_and_generate_bst.py", line 50, in num_bst1
    return recursive(n)
  File "count_and_generate_bst.py", line 46, in recursive
    num += recursive(i-1) * recursive(n-i)
  File "count_and_generate_bst.py", line 46, in recursive
    num += recursive(i-1) * recursive(n-i)
  File "count_and_generate_bst.py", line 46, in recursive
    num += recursive(i-1) * recursive(n-i)
  [Previous line repeated 21 more times]
  File "count_and_generate_bst.py", line 45, in recursive
    for i in range(1, n+1):
KeyboardInterrupt

n=33时,单纯的递归已经好几秒算不出来了,去掉它,只用另外两种算法:

num_bst cost time: 5.245208740234375e-06
num_bst2 cost time: 4.5299530029296875e-06
5 5
num_bst cost time: 1.8358230590820312e-05
num_bst2 cost time: 3.123283386230469e-05
1430 1430
num_bst cost time: 1.4543533325195312e-05
num_bst2 cost time: 5.936622619628906e-05
742900 742900
num_bst cost time: 0.00011610984802246094
num_bst2 cost time: 0.0001766681671142578
212336130412243110 212336130412243110

进阶

生成这些树,返回生成的这些树的根节点。

def generate(n):
    def clone_tree(root):
        if root is None:
            return None

        r = TreeNode(root.val)
        r.left = clone_tree(root.left)
        r.right = clone_tree(root.right)

        return r

    def recursive(start, end):
        res = []
        if start > end:
            return [None]

        root = None
        for i in range(start, end+1):
            root = TreeNode(i)
            left_subs = recursive(start, i-1)
            right_subs = recursive(i+1, end)
            for l in left_subs:
                for r in right_subs:
                    root.left, root.right = l, r
                    res.append(clone_tree(root))

        return res

    return recursive(1, n)

测试

def print_tree_by_level(root):
    if root is None:
        return

    q = [root, None]
    level = 0
    print('level %d: ' % (level), end='')
    while q:
        node = q.pop(0)
        if node is None:
            if q:
                level += 1
                q.append(None)
                print()
                print('level %d: ' % (level), end='')
        else:
            print(node.val, end=', ')
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
    print()
    print('------------------------')


def test(n):
    n0 = num_bst(n)
    #n1 = num_bst1(n)
    n2 = num_bst2(n)
    #print(n0, n1, n2)
    print(n0, n2)


def test_generate(n):
    results = generate(n)
    for root in results:
        print_tree_by_level(root)


if __name__ == '__main__':
    test(3)
    test(8)
    test(13)
    test(33)
    test_generate(3)

结果

num_bst cost time: 5.245208740234375e-06
num_bst2 cost time: 4.5299530029296875e-06
5 5
num_bst cost time: 1.8358230590820312e-05
num_bst2 cost time: 3.123283386230469e-05
1430 1430
num_bst cost time: 1.4543533325195312e-05
num_bst2 cost time: 5.936622619628906e-05
742900 742900
num_bst cost time: 0.00011610984802246094
num_bst2 cost time: 0.0001766681671142578
212336130412243110 212336130412243110
level 0: 1, 
level 1: 2, 
level 2: 3, 
------------------------
level 0: 1, 
level 1: 3, 
level 2: 2, 
------------------------
level 0: 2, 
level 1: 1, 3, 
------------------------
level 0: 3, 
level 1: 1, 
level 2: 2, 
------------------------
level 0: 3, 
level 1: 2, 
level 2: 1, 
------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.实验目的 通过“图片压缩编码”的编程实践,学习树、遍历二叉树、哈夫曼树、哈夫曼编码和他们的编程应用。 (1)掌握树的存储结构 (2)掌握二叉树的三种遍历方法 (3)掌握并理解Huffman树、Huffman编码等知识和应用 (4)掌握文件的操作 (5)使用Huffman算法实现图像压缩程序 2.实验内容 使用Huffman压缩算法,对一幅BMP格式的图片文件进行压缩。图片文件名为“Pic.bmp”,压缩后保存为“Pic.bmp.huf”文件。使用VS2010作为开发工具,开发一个控制台程序,使用Huffman压缩算法对图片文件“Pic.bmp”进行压缩。具体要如下: (1)读取原文件,统计权值: 运行程序,输入文件名。 以“Pic.bmp”文件为例。若文件存放在F盘根目录下,输入文件完整路径“F:\Pic.bmp”。按回车结束。以字节流的方式,只读打“Pic.bmp”文件。 逐字节读取文件,统计文件中256种字节重复的次数,保存到一个数组中int weight[256]中。 (2)生成Huffman树 根据(1)中统计的结果,构建Huffman树。定义一个结构体来记录每个节点的权值、父节点、左孩子和右孩子。使用结构体数组来存储这个Huffman树。 (3)生成Huffman编码 遍历(2)中生成的Huffman树,记录256个叶子节点的Huffman编码,保存在字符串数组中。 (4)压缩原文件 使用Huffman编码对原文件中的字节重新编码,获得压缩后的文件数据。 (5)保存压缩文件 将编码过的数据,保存到文件“Pic.bmp.huf”中。
哈夫曼树是一种二叉树,它的叶子节点对应于符号集中的每个符号,并且每个符号的出现频率成为该叶子节点的权值。哈夫曼树的构造过程是通过反复选择两个权值最小的节点来构建的,直到所有节点都被合并成为根节点。构建好的哈夫曼树可以用来生成哈夫曼编码,即对于每个符号,将其在哈夫曼树上的路径转换为二进制编码,从而使得权值较小的符号具有较短的编码,权值较大的符号具有较长的编码,从而达到压缩数据的目的。 具体的哈夫曼树和哈夫曼编码的构造流程如下: 1. 统计每个符号在数据中出现的频率,将其作为叶子节点的权值,构建出一个只包含叶子节点的森林。 2. 从森林中选择权值最小的两个节点,将它们合并成一个新的节点,并将其权值设为两个节点权值之和。 3. 将新的节点插入到森林中,从森林中删除原来的两个节点。 4. 重复步骤2和步骤3,直到森林中只剩下一个节点为止,这个节点就是哈夫曼树的根节点。 5. 对于哈夫曼树中的每个叶子节点,从根节点开始沿着路径向下走,每当走到一个左子节点时,将当前的编码位设为0,每当走到一个右子节点时,将当前的编码位设为1,直到到达叶子节点为止,此时叶子节点的编码就是从根节点到达该节点所经过的所有编码位组成的二进制编码。 6. 将每个符号的编码记录下来,即可得到哈夫曼编码。 需要注意的是,哈夫曼树和哈夫曼编码的构造过程是基于数据中每个符号的频率来进行的,因此在不同的数据集中,哈夫曼树和哈夫曼编码可能会有所不同

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值