Pyhton-Leetcode-二叉搜索树(二叉排序树、二叉查找树)相关题目

二叉搜索树是一种所有左子树小于其父节点,右子树大于其父节点的特殊二叉树。

二叉搜索树主要用来出检索某个节点。【用于搜索】

二叉搜索树按照中序遍历,就能够得出正确从小到大的序列排序。

面试题54. 二叉搜索树的第k大节点

考点:二叉搜索树的性质。首先要知道的就是二叉搜索树中序遍历,就是从小到大的序列排序。那么就可以遍历一遍,然后输出k大就行了。BFS+list.sort()  是解法1,很慢。

解法2:递归和非递归的中序遍历

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        #########非递归##########
        if not root: return 0 
        L = []
        q = []
        node = root
        while(node or q):
            if node:                 ## while(node):q.append(node) node.left 
                q.append(node)
                node= node.left
            else:
                node = q.pop() #最深的那个 
                L.append(node.val) #最左
                node = node.right #转到右子树
            if len(L)
        return L[-k]  ##从小到大的 
        
        ############递归####################
        if not root :return 0 #
        L = []
        def t(node):
            if node.left:
                t(node.left)
            L.append(node.val)
            if node.right:
                t(node.right)
        t(root)
        return L[-k]

解法3(解法2的优化)

解法2 O(N) O(N) 因为求解的是K大,右子树的值肯定要比左子树大,所以可以按照右-中-左的顺序遍历,当list长度等于K时,立即停止,以节省时间。

递归和非递归: 来自此处的@JustYou


class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:        
        ##非递归版本 @JustYou
        if not root :return 0 
        q = []
        count = 0 
        node = root ##不改变原结构
        while(node or q):
            while(node): #直接找到最右的
                q.append(node)
                node = node.right #从右开始 
            if q: #添加最右的信息
                node = q.pop() #最深的那个 
                count +=1
                if count == k : return node.val ##不用保存前k-1个节点,以节省空间
                node = node.left #转到左子树
        
        ####递归版本##########
        #递归版本
        if not root:return 0 #没有结果返回
        self.count = 0 ##全局
        self.ans = 0  
        def t(node,k):
            if node:
                t(node.right,k)
                self.count += 1 ## 从上到下
                if self.count == k:
                    self.ans = node.val
                    return 
                t(node.left,k)
        t(root,k)
        return self.ans

剑指 Offer 33. 二叉搜索树的后序遍历序列

二叉搜索树的正确后序遍历的倒序 是 根节点,右子树,左子树。那么按照搜索树的关系,那么右子树中的每个节点应该大于根节点。左子树中的每个节点都应该小于根节点。那么根据根节点可以先将左右子树分开,然后再继续在每颗子树上面,继续做递归操作。

自己写的:time 80%。 

class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        # 二叉树的后序遍历 
        order = postorder[::-1] # 倒数排序
        # 根  右  左
        def judge(order):
            if len(order) == 0:return True 
            index = order[0]
            i = 1 
            while(i<len(order)):      
                if order[i] > index:
                    i+=1
                else:
                    break 
            left  = order[i:] ## 划分左右子树
            right = order[1:i] 
            for i in range(0,len(left)): 
                if left[i] > index:return False ## 判断此轮是否满足条件
            for j in range(0,len(right)):
                if right[j] < index:return False
            return judge(left) and judge(right) ## 此轮满足前提下,再继续深入判断,左右都满足才可以 
        return judge(order)

看了评论和题解。总结一下不足:

(1)没必要占用left 和 right 来实现左右划分,直接利用i的值就可以进行划分,传入下一轮order[i:]  order[1:i] 

(2) 没必要在判断右子树是否满足条件,因为它肯定是满足的,就是按照i划分的。只需要判断左子树是否满足条件就行。

改后:

class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        # 二叉树的后序遍历 
        order = postorder[::-1] # 倒数排序
        # 根  右  左
        def judge(order):
            if len(order) == 0:return True 
            index = order[0]
            i = 1 
            while(i<len(order)):
                if order[i] < index:break 
                i+=1
            #left  = order[i:] ## 划分左右子树
            #right = order[1:i] 
            #for i in range(0,len(left)):
                #if left[i] > index:return False ## 判断此轮是否满足条件
            for j in range(i,len(order)):
                if order[j] > index:return False
            return judge(order[i:]) and judge(order[1:i]) 
            ## 此轮满足前提下,再继续深入判断,左右都满足才可以 
        return judge(order)  

(3)后序遍历的顺序是 左 右 根 那么不用倒序,直接利用最后一个节点来区分左右子树判断就行。

下面代码参考了此题评论中的@Mars的思路。 time:98%。

        def judge(nums):
            if len(nums) == 0:return True
            index = nums[-1]
            for i in range(0,len(nums)):
                if nums[i] > index:break # 分隔出了左子树
            for j in range(i,len(nums)):
                if nums[j] < index:return False # 继续分隔右子树,如果不满足条件直接跳出,满足完成此次划分
            return judge(nums[:i]) and judge(nums[i:-1]) # 左子树,右子树 必须全部满足 
        return judge(postorder)

(4)如果按照后序遍历的逆序是 root->right>left。那么可以构造一个单调栈来维护。思想来自评论中的此题解@失火的夏天。代码来自:此链接

主要思想就是:后序遍历的倒序 ->根 右 左。那么刚开始是一个单调增的单调栈,后面是一个单调减的单调栈。在正确的情况下,入栈的元素一定小于根节点。(第一层的根节点设置为 正无穷)。每一次转换单调增/减时,重新寻找root。如果完成了,就是正确的,否则错误。

class Solution:
    def verifyPostorder(self, postorder: [int]) -> bool:
        stack, root = [], float("+inf")
        for i in range(len(postorder) - 1, -1, -1):
            if postorder[i] > root: return False
            while(stack and postorder[i] < stack[-1]):
                root = stack.pop()
            stack.append(postorder[i])
        return True

剑指 Offer 36. 二叉搜索树与双向链表

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Foneone

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值