• 专题:二叉搜索树


    什么事二叉搜索数

    leetcode98. 验证二叉搜索树
    给定一个二叉树,判断其是否是一个有效的二叉搜索树。

    假设一个二叉搜索树具有如下特征:

    节点的左子树只包含小于当前节点的数。
       节点的右子树只包含大于当前节点的数。
       所有左子树和右子树自身必须也是二叉搜索树。

    示例 1:

    输入:
       2
      /
     1   3
    输出: true

    示例 2:

    输入:
       5
      /
     1   4
        /
       3   6
    输出: false
    解释: 输入为: [5,1,4,null,null,3,6]。
        根节点的值为 5 ,但是其右子节点值为 4 。

    这道题么考频不是很高,但诠释了二叉搜索树的概念,掌握起来也是很快的,是二叉搜索树的基础题
    做完了就知道二叉搜索树大概是个什么东西

    python通过代码

    class Solution:
        def isValidBST(self, root: TreeNode) -> bool:
            return self.helper(root,None,None)
        def helper(self,root,low,high):
            if not root: return True
            if low != None and root.val <= low: return False
            if high != None and root.val >= high: return False
            if not self.helper(root.left,low,root.val): return False
            if not self.helper(root.right,root.val,high): return False
            return True
    

    规模为n的二叉搜索树一共有多少种?

    leetcode96. 不同的二叉搜索树
    95,96是二叉树标签下考频最高的两道题,尤其重要,必须牢固掌握
    给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

    示例:

    输入: 3
    输出: 5
    解释:
    给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

    1         3     3      2      1
              /     /      /      
        3     2     1      1   3      2
       /     /                      
      2     1         2                 3

    这道题用dp去做,
    状态转移方程看东北程序员视频里的推导
    图片.png
    这个递推式有必要记下来,因为这是卡特兰数的递推式
    在leetcode中碰到的组合题,前几项n=1,2,3,4的时候 结果数列为1,2,5,14,...
    看到1,2,5,14就要敏感,这个东西基本就是卡特兰数了,因为他不会给你太弱智的找规律
    那知道这个是卡特兰数之后,问题就迎刃而解了

    class Solution:
        def numTrees(self, n: int) -> int:
            dp = [1] * (n+1)
            for i in range(1,n+1):
                sum_ = 0
                for j in range(i):
                    sum_ += dp[j]*dp[i-j-1]
                dp[i] = sum_
            return dp[-1]
    

    总结 卡特兰数记两个东西
    1.通项公式(也就是题目dp中的状态转移方程)
    2.前四项1,2,5,14
    周赛如果遇到了,直接编,想都不要想,碰运气,编不出来拉倒
    如果不放心,基本也可以顺着卡特兰数通项的思路去想前后两个数之间的关系,逻辑理顺了就能确保是卡特兰数了

    95. 不同的二叉搜索树 II

    这道题比96更难一点,相比96他要生成所有的二叉树,但可以按96如法炮制,用很复杂的dp去做(嵌套了四层for循环),实际上时间么在可接受范围之内,卡的不是很严
    图片.png
    先看题目吧

    给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。

    示例:

    输入: 3
    输出:
    [
     [1,null,3,2],
     [3,2,null,1],
     [3,1,null,null,2],
     [2,1,3],
     [1,null,2,null,3]
    ]
    解释:
    以上的输出对应以下 5 种不同结构的二叉搜索树:

    1         3     3      2      1
              /     /      /      
        3     2     1      1   3      2
       /     /                      
      2     1         2                 3

    如果刚做完96再来做95 思路是很明晰的

    class Solution:
        def generateTrees(self, n: int) -> List[TreeNode]:
            dp = [[None]] * (n+1)
            if not n: return []
            for i in range(1,n+1):
                dp[i] = []
                for j in range(i):
                    for lefttree in dp[j]:
                        for righttree in dp[i-j-1]:
                            thistree = TreeNode(j+1)
                            thistree.left = lefttree
                            thistree.right = self.copytree(righttree,j+1)
                            dp[i].append(thistree)
            return dp[-1]
        def copytree(self,root,k):
            if not root: return None
            res = TreeNode(root.val+k)
            res.left = self.copytree(root.left,k)
            res.right = self.copytree(root.right,k)
            return res
    

    需要注意的点是,左子树可以直接引用传参,但右子树需要给每个节点加上数值
    比如1234567 取j=3

    你把4作为root,左子树递归调用dp[3]
    右子树也调用dp[3],但出来的子树是123,而你需要的右子树范围是567
    所以这里每个节点的数值加上j+1(3+1=4)进行深拷贝
    额外实现了一个深拷贝+更改节点数值的函数在下面
    这个copy函数很简单了,二叉树遍历法的常规操作
    这道题不止这一个方法,还有一个方法请看东北程序员
    https://www.bilibili.com/video/av68783564?p=93
    第二种写法就是DFS+递归,也许面试官问到更想听这种方法,比第一种代码简洁很多

    class Solution:
        def generateTrees(self, n: int) -> List[TreeNode]:
            if not n: return []
            return self.helper(1,n)
        def helper(self,i,j):
            if j < i: return [None]
            res = []
            for idx in range(i,j+1):
                left = self.helper(i,idx-1)
                right = self.helper(idx+1,j)
                for lefttree in left:
                    for righttree in right:
                        thistree = TreeNode(idx)
                        thistree.left = lefttree
                        thistree.right = righttree
                        res.append(thistree)
            return res
    
    

    一个坑:这里我建议把helper的i,j换成start,end或者left,right. 因为我一开始把其中一个i打成1了,报错找了很久才找到,由此可见命名规范的重要性
    时间也比上一种少
    执行结果:
    通过
    显示详情
    执行用时 :52 ms, 在所有 python3 提交中击败了96.03% 的用户
    内存消耗 :14 MB, 在所有 python3 提交中击败了95.19%的用户

    99. 恢复二叉搜索树

    二叉搜索树中的两个节点被错误地交换。

    请在不改变其结构的情况下,恢复这棵树。

    示例 1:

    输入: [1,3,null,null,2]

    1
     /
    3
     
      2

    输出: [3,1,null,null,2]

    3
     /
    1
     
      2

    示例 2:

    输入: [3,1,4,null,null,2]

    3
    /
    1   4
      /
     2

    输出: [2,1,4,null,null,3]

    2
    /
    1   4
      /
     3

    进阶:

    使用 O(n) 空间复杂度的解法很容易实现。
       你能想出一个只使用常数空间的解决方案吗?

    其实就遍历一遍,用全局变量记录,因为函数递归调用起来记录变量顺序很麻烦,必须用全局变量脱离递归套用的约束,省去传参的麻烦

    class Solution:
    
        def recoverTree(self, root: TreeNode) -> None:
            """
            Do not return anything, modify root in-place instead.
            """
            self.err1 = None
            self.err2 = None
            self.pre = None
            def helper(root):
                if not root: return
                helper(root.left)
                if self.pre and self.pre.val > root.val and not self.err1:
                    self.err1 = self.pre
                if self.err1 and self.pre.val > root.val:
                    self.err2 = root
                self.pre = root
                helper(root.right)
            helper(root)
            self.err1.val,self.err2.val = self.err2.val,self.err1.val
    

    这里python用的是类的属性变量来模拟全局变量(因为函数内部也可以直接作用类的属性变量)
    凡是可以递归的东西必然可以迭代,而这题使用迭代就不需要全局变量的概念了

  • 相关阅读:
    Lucky Coins Sequence
    A == B ?
    Chinese Rings
    51nod 1051 最大子矩阵和
    51nod 1103 N的倍数
    Codeforces Round #429 (Div. 2)
    51nod 1043 幸运号码(数位dp
    51nod 1266 蚂蚁
    51nod 1090 3个数和为0
    51nod 1082 与7无关的数
  • 原文地址:https://www.cnblogs.com/Akarinnnn/p/12091345.html
Copyright © 2020-2023  润新知