剑指offer系列-面试题33-二叉搜索树的后序遍历(python)

1. 题目

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回True,否则返回False,假设输入的数组的任意数字都互不相同。例如,输入数组{5,7,6,9,11,10,8},则返回True,因为这个整数序列是图中二叉树的搜索树的后序遍历结果。如果输入的数组是{7,4,6,5},则由于没有哪棵二叉搜索树的后序遍历结果是这个序列,因此返回False。


5
6
7
8
9
10
11

2. 解题思路

看过了题目之后,我存在几个疑问?

  1. 什么是二叉搜索树?他有什么特点?

通俗地讲,二叉搜索树也叫二叉排序树,他的特点就是左子树的节点都小于根节点,右子树的节点都大于根节点,而且这个规律对于每一棵子树都成立。

  1. 什么是后序遍历?他有什么特点?

后序遍历是左-右-根。举几个例子就会发现,他的特点就是根节点是最后一个元素,而且左、右子树的节点是分开的,也就是说当你找到第一个大于根节点的值,理论上这个值到序列中根节点的前一个值都是属于右子树的。这个规律对于每一棵子树都成立。

  1. 扩展
    什么是前序遍历?什么是中序遍历?
    前序遍历就是根-左-右,特点就是根节点是第一个元素,而且对于每一棵子树都成立。中序遍历是左-根-右,特点是根节点在中部,只要我们知道了根节点是谁,其左边就是左子树中的元素,其右边就是右子树中的元素。

其实只要知道了上面这两个问题的答案,思路肯定就有了。

3. 代码实现

3.1 递归版


import time

def verify_sequence_of_BST(sequence):
    """
    验证此序列是否可以是某一个二叉搜索树的后序遍历序列
    :param squence: 序列
    :return True/False
    """
    # 1.先判断序列是否为None或空
    if not sequence:
        return False
    
    i = 0
    root = sequence[-1] # 理论上,后续遍历序列中最后一个元素是根节点
    # 2.遍历序列(除最后一个节点外),根据大于根节点的值和小于根节点的值,分成两个子序列
    # 遍历序列找到左子树和右子树的分界点。第一个大于根节点的节点是右子树的第一个节点。
    while i < len(sequence)-1:
        print('i的值是{}'.format(i))
        if sequence[i] > root:
            break
        i += 1
    j = i
    # 若理论上应该都大于根节点的这些节点中,存在小于根节点的,则说明此序列不是二叉搜索树的后序遍历序列
    while j < len(sequence)-1:
        print('j的值是{}'.format(j))
        if sequence[j] < root:
            return False
        j += 1
    left = True
    # 判断左子树是不是二叉搜索树
    # 因为对list进行分片,因为左、右子序列是一直在变小,因此i必须要大于0,小于len(sequence)-1
    if i > 0:
        left = verify_sequence_of_BST(sequence[:i])
    # 判断右子树是不是二叉搜索树
    right = True
    if i < len(sequence)-1:
        right = verify_sequence_of_BST(sequence[i:-1])
    return  left and right


3.2 循环版

那就有人要说了,面试官说递归效率太低了,能不用递归来实现吗?用队列应该可以实现的把

def verify_sequence_of_BST(sequence):
	"""
    验证此序列是否可以是某一个二叉搜索树的后序遍历序列
    :param squence: 序列
    :return True/False
    """
    # 1.先判断序列是否为None或空
    if not sequence:
        return False
        
    queue = [] # 定义一个队列
    queue.append(sequence) # 将其实序列加入队列中

    while queue:
        print("队列长度{}".format(queue))
        seq = queue.pop(0)
       
        i = 0
        root = seq[-1] # 理论上,后续遍历序列中最后一个元素是根节点
        # 2.遍历序列(除最后一个节点外),根据大于根节点的值和小于根节点的值,分成两个子序列
        # 遍历序列找到左子树和右子树的分界点。第一个大于根节点的节点是右子树的第一个节点。
        while i < len(seq)-1:
            print('i的值是{}, 总长度{}'.format(i, len(seq)))
            if seq[i] > root:
                break
            i += 1
            time.sleep(1)
        j = i
        # 若理论上应该都大于根节点的这些节点中,存在小于根节点的,则说明此序列不是二叉搜索树的后序遍历序列
        while j < len(seq)-1:
            print('j的值是{},总长度{}'.format(j, len(seq)))
            if seq[j] < root:
                return False
            j += 1
            time.sleep(1)
        
        # 因为对list进行分片,因为左、右子序列是一直在变小,因此i必须要大于0,小于len(sequence)-1
        # 判断左子树是不是二叉搜索树
        if i > 0:
            queue.append(seq[:i])
        # 判断右子树是不是二叉搜索树
        if i < len(seq)-1:
            queue.append(seq[i:-1])
    return True


if __name__ == '__main__':
    s = [5, 7, 6, 9, 11, 10, 8]
    #res1 = verify_sequence_of_BST_1(s)
    res2 = verify_sequence_of_BST_2(s)

    # print(res1)
    print(res2)

4. 总结

解决这道题的关键就是要知道什么二叉搜索树,特点是什么?什么后序遍历,特点是什么?

反正就是二叉树我就容易想到用队列来解决。大部分好像都可以。

5. 参考文献

[1] 剑指offer丛书

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值