• LeetCode Cookbook 树(3)


    CSDN话题挑战赛第2期
    参赛话题:算法题解

    LeetCode Cookbook 树(3)

       本节内容主要与 二叉搜索树 相关。

    96. 不同的二叉搜索树(model-VI 二叉搜索树)

    题目链接:96. 不同的二叉搜索树
    题目大意:给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

    例如:
    在这里插入图片描述

    输入:n = 3
    输出:5
    
    • 解题思路:动态规划 左子树个数 与 右子树个数 的乘积。
    • 时间复杂度: O ( N 2 ) O(N^2) O(N2) N为二叉搜索树的结点数
    • 空间复杂度: O ( N ) O(N) O(N)
    class Solution:
        def numTrees(self, n: int) -> int:
            dp = [0]*(n+1)
            dp[0],dp[1] = 1,1
            for i in range(2,n+1):
                for j in range(1,i+1):
                    # 左子树结点个数为 j-1 右子树结点个数i-j
                    dp[i] += dp[j-1]*dp[i-j]
                #     print(dp)
                # print('-------------------')
            return dp[n]
    

    95. 不同的二叉搜索树 II(model-VI 二叉搜索树)

    题目链接:95. 不同的二叉搜索树 II
    题目大意:给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。

    例如:
    在这里插入图片描述

    输入:n = 3
    输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]
    

    解题思路: DFS 来做这道题, 需要注意每一次都需要建树的操作 for 循环中 套 双 for 循环。

    • 时间复杂度: O ( 4 n n 1 / 2 ) ) O(\frac{4^n}{n^{1/2}})) O(n1/24n)) 时间复杂度与空间复杂度 和 卡特兰数 相关
    • 空间复杂度: O ( 4 n n 1 / 2 ) ) O(\frac{4^n}{n^{1/2}})) O(n1/24n))
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def generateTrees(self, n: int) -> List[Optional[TreeNode]]:
    
            def generate(start: int,end: int) -> List[List[int]]:
                if start > end: return [None,]
                ans = list()
                for i in range(start,end+1):
                    # 左子树合集 右子树合集
                    leftTree = generate(start,i-1)
                    rightTree = generate(i+1,end)
    
                    # 拼树 左子树 右子树 和 一个根结点
                    for L in leftTree:
                        for R in rightTree:
                            curTree = TreeNode(i)
                            curTree.left = L
                            curTree.right = R 
                            ans.append(curTree)
                return ans
            return generate(1,n) if n else []
    

    99. 恢复二叉搜索树(model-VI 二叉搜索树)

    题目链接:99. 恢复二叉搜索树
    题目大意:给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。

    例如:
    在这里插入图片描述

    输入:root = [1,3,null,null,2]
    输出:[3,1,null,null,2]
    解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 13 使二叉搜索树有效。
    

    在这里插入图片描述

    输入:root = [3,1,4,null,null,2]
    输出:[2,1,4,null,null,3]
    解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 23 使二叉搜索树有效。
    
    • 解题思路: 中序遍历 结合二叉搜索树的特性 即: 左子树 根节点 右子树 找不符合要求的两个结点,而后 交换两个结点的值即可。
    • 时间复杂度: O ( N ) O(N) O(N) N为二叉搜索树的结点数。
    • 空间复杂度: O ( H ) O(H) O(H) H为树的高度,中序遍历栈的深度取决于树的高度。
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def __init__(self):
            self.first,self.second = None,None
            self.preNode = TreeNode(float('-inf'))
    
        def inOrder(self,root: Optional[TreeNode]) -> None:
            if root is None: return 
            self.inOrder(root.left)
            if self.first == None and self.preNode.val >= root.val:
                self.first = self.preNode
            if self.first and self.preNode.val >= root.val:
                self.second = root
            self.preNode = root
            self.inOrder(root.right)
    
        def recoverTree(self, root: Optional[TreeNode]) -> None:
            """
            Do not return anything, modify root in-place instead.
            """
            self.inOrder(root)
            self.first.val,self.second.val = self.second.val,self.first.val
    

    114. 二叉树展开为链表(model-VI 二叉搜索树)

    题目链接:114. 二叉树展开为链表
    题目大意:给你二叉树的根结点 root ,请你将它展开为一个单链表:

    • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
    • 展开后的单链表应该与二叉树 先序遍历 顺序相同

    例如:
    在这里插入图片描述

    输入:root = [1,2,5,3,4,null,6]
    输出:[1,null,2,null,3,null,4,null,5,null,6]
    
    • 解题思路: 按题意一步一步做 即可。
    • 时间复杂度: O ( N ) O(N) O(N) N为二叉搜索树的结点数
    • 空间复杂度: O ( N ) O(N) O(N)
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def flatten(self, root: Optional[TreeNode]) -> None:
            """
            Do not return anything, modify root in-place instead.
            """
            # 根左右 前序遍历
            res = list()
            
            def preOrder(root: Optional[TreeNode]) -> None:
                if root is None: return 
                res.append(root)
                preOrder(root.left)
                preOrder(root.right)
            
            preOrder(root)
            n = len(res)
            for i in range(n-1):
                prev,curr = res[i],res[i+1]
                prev.left = None
                prev.right = curr
    

    173. 二叉搜索树迭代器(model-VI 二叉搜索树)

    题目链接:173. 二叉搜索树迭代器
    题目大意:实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:

    • BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
    • boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
    • int next()将指针向右移动,然后返回指针处的数字。
      注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。
      你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

    例如:
    在这里插入图片描述

    输入
    ["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
    [[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
    输出
    [null, 3, 7, true, 9, true, 15, true, 20, false]
    
    解释
    BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
    bSTIterator.next();    // 返回 3
    bSTIterator.next();    // 返回 7
    bSTIterator.hasNext(); // 返回 True
    bSTIterator.next();    // 返回 9
    bSTIterator.hasNext(); // 返回 True
    bSTIterator.next();    // 返回 15
    bSTIterator.hasNext(); // 返回 True
    bSTIterator.next();    // 返回 20
    bSTIterator.hasNext(); // 返回 False
    
    • 解题思路: 借助 双头队列 建树。
    • 时间复杂度:构造函数 O ( N ) O(N) O(N) N为二叉搜索树的结点数 ;next()方法为 O ( 1 ) O(1) O(1)
    • 空间复杂度: O ( N ) O(N) O(N)
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class BSTIterator:
    
        def __init__(self, root: Optional[TreeNode]):
            self.q = collections.deque()
            self.inOrder(root)
    
        def inOrder(self,root) -> None:
            if root is None: return
            self.inOrder(root.left)
            self.q.append(root.val)
            self.inOrder(root.right)
    
        def next(self) -> int:
            return self.q.popleft()
    
        def hasNext(self) -> bool:
            return len(self.q)>0
    
    # Your BSTIterator object will be instantiated and called as such:
    # obj = BSTIterator(root)
    # param_1 = obj.next()
    # param_2 = obj.hasNext()
    

    1382. 将二叉搜索树变平衡(model-VI 扩展)

    题目链接:1382. 将二叉搜索树变平衡
    题目大意:给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法,请你返回任意一种。
    如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是 平衡的 。

    例如:
    在这里插入图片描述

    输入:root = [1,null,2,null,3,null,4,null,null]
    输出:[2,1,3,null,null,null,4]
    解释:这不是唯一的正确答案,[3,1,4,null,2,null,null] 也是一个可行的构造方案。
    
    • 解题思路: 中序遍历二叉树 二分法建树。
    • 时间复杂度: O ( N ) O(N) O(N) N为二叉搜索树的结点数
    • 空间复杂度: O ( N ) O(N) O(N)
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def balanceBST(self, root: TreeNode) -> TreeNode:
            def inOrder(o: TreeNode):
                if o.left: inOrder(o.left)
                inOrderSeq.append(o.val)
                if o.right: inOrder(o.right)
            
            def build(L,R) -> TreeNode:
                if L>R: return None
                mid = L+(R-L)//2
                o = TreeNode(inOrderSeq[mid])
                o.left = build(L,mid-1)
                o.right = build(mid+1,R)
                return o 
                
            inOrderSeq = list()
            inOrder(root)
            return build(0,len(inOrderSeq)-1)
    

    653. 两数之和 IV - 输入二叉搜索树(model-VI 扩展)

    题目链接:653. 两数之和 IV - 输入二叉搜索树
    题目大意:给定一个二叉搜索树 root 和一个目标结果 k,如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果,则返回 true。

    例如:
    在这里插入图片描述

    输入: root = [5,3,6,2,4,null,7], k = 9
    输出: true
    
    输入: root = [5,3,6,2,4,null,7], k = 28
    输出: false
    
    • 解题思路:DFS
    • 时间复杂度: O ( N ) O(N) O(N) N为二叉搜索树的结点数
    • 空间复杂度: O ( N ) O(N) O(N)
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def __init__(self):
            self.s = set()
    
        def findTarget(self, root: Optional[TreeNode], k: int) -> bool:
            if root is None: return False
            if k-root.val in self.s:
                return True
            self.s.add(root.val)
            return self.findTarget(root.left,k) or self.findTarget(root.right,k)
    

    112. 路径总和**(model-VII 路径)

    题目链接:112. 路径总和
    题目大意:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
    叶子节点 是指没有子节点的节点。

    例如:
    在这里插入图片描述

    输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
    输出:true
    解释:等于目标和的根节点到叶节点路径如上图所示。
    
    • 解题思路: DFS 与653. 两数之和 IV - 输入二叉搜索树相近 这里注意叶子结点的判断方式 非常典型 后续会用到很多。
    • 时间复杂度: O ( N ) O(N) O(N) N为树的结点数
    • 空间复杂度: O ( H ) O(H) O(H) H为树的高度
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def hasPathSum(self, root: Optional[TreeNode], tSum: int) -> bool:
            if root is None: return False
            if root.left is None and root.right is None: return root.val == tSum
            return self.hasPathSum(root.left,tSum-root.val) or \
            self.hasPathSum(root.right,tSum-root.val)
    

    257. 二叉树的所有路径 **(model-VII 路径)

    题目链接:257. 二叉树的所有路径
    题目大意:给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。

    例如:
    在这里插入图片描述

    输入:root = [1,2,3,null,5]
    输出:["1->2->5","1->3"]
    
    • 解题思路: DFS 或 BFS。
    • 时间复杂度: O ( N 2 ) O(N^2) O(N2) N为二叉树的结点数
    • 空间复杂度: O ( N 2 ) O(N^2) O(N2)

    方法(一)

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
    
            def dfs(root:Optional[TreeNode],path:str) -> None:
                if root: 
                    path += str(root.val)   
                    if root.left is None and root.right is None:
                        paths.append(path) 
                    else:
                        path += '->' 
                        dfs(root.left,path)
                        dfs(root.right,path)
                    
            paths = list()
            dfs(root,"")
            return paths
    

    方法(二)

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
          
            paths = list()
            if root is None: return paths
            q,qPath = list([root]),list([str(root.val)])
            while q:
                node,path = q.pop(),qPath.pop()
                if node.left is None and node.right is None:
                    paths.append(path)
                else:
                    if node.left:
                        q.append(node.left)
                        qPath.append(path + '->' + str(node.left.val))
                    if node.right:
                        q.append(node.right)
                        qPath.append(path + '->' + str(node.right.val))
            return paths
    

    129. 求根节点到叶节点数字之和(model-VII 路径)

    题目链接:129. 求根节点到叶节点数字之和
    题目大意:给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
    每条从根节点到叶节点的路径都代表一个数字:

    • 例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
      计算从根节点到叶节点生成的 所有数字之和 。
      叶节点 是指没有子节点的节点。

    例如:
    在这里插入图片描述

    输入:root = [1,2,3]
    输出:25
    解释:
    从根到叶子节点路径 1->2 代表数字 12
    从根到叶子节点路径 1->3 代表数字 13
    因此,数字总和 = 12 + 13 = 25
    
    • 解题思路: 与 257. 二叉树的所有路径 一样的思路,不过对于结点的值处理手段不同。
    • 时间复杂度: O ( N ) O(N) O(N) N为二叉树的结点数
    • 空间复杂度: O ( N ) O(N) O(N)
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def sumNumbers(self, root: Optional[TreeNode]) -> int:
            # DFS
            def dfs(node: Optional[TreeNode],num: int) -> None:
                if node:
                    num = 10*num + node.val
                    if node.left is None and node.right is None:
                        paths.append(num)
                    else:
                        dfs(node.left,num)
                        dfs(node.right,num)
    
            paths = list()
            num = 0
            dfs(root,num)
            # print(paths)
            return sum(paths)
    

    113. 路径总和 II(model-VII 路径)

    题目链接:113. 路径总和 II
    题目大意:给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

    • 叶子节点 是指没有子节点的节

    例如:
    在这里插入图片描述

    输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
    输出:[[5,4,11,2],[5,8,4,5]]
    
    • 解题思路: DFS 这里注意了 append 函数 只能添加单个元素 一定要用 [:] 复制整个数组元素。
    • 时间复杂度: O ( N 2 ) O(N^2) O(N2) N为二叉树的结点数
    • 空间复杂度: O ( N ) O(N) O(N)
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
            
            def dfs1(root:Optional[TreeNode],tSum: int) -> bool:
                if root is None: return False
                if root.left is None and root.right is None:
                    return root.val == tSum
                return dfs1(root.left,tSum-root.val) or dfs1(root.right,tSum-root.val)
    
            
            def dfs2(root:Optional[TreeNode],tSum: int) -> None:
                if root:
                    path.append(root.val)
                    tSum -= root.val
                    if root.left is None and root.right is None and tSum == 0:
                        # 搞明白 append 加入的是一个元素 需要[:]
                        paths.append(path[:])
                    dfs2(root.left,tSum)
                    dfs2(root.right,tSum)
                    path.pop()
    
            paths = list()
            path = list()
            if dfs1(root,targetSum):
                dfs2(root,targetSum)
                return paths
            else:
                return []
    

    437. 路径总和 III(model-VII 路径)

    题目链接:437. 路径总和 III
    题目大意:
    给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

    • 路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

    例如:
    在这里插入图片描述

    输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
    输出:3
    解释:和等于 8 的路径有 3 条,如图所示。
    
    • 解题思路: 双重 DFS
    • 时间复杂度: O ( N 2 ) O(N^2) O(N2) N为二叉树的结点数
    • 空间复杂度: O ( N ) O(N) O(N)
    # # Definition for a binary tree node.
    # # class TreeNode:
    # #     def __init__(self, val=0, left=None, right=None):
    # #         self.val = val
    # #         self.left = left
    # #         self.right = right
    class Solution:
        def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
    
            def rootSum(node: Optional[TreeNode],tSum: int) -> int:
                if node is None: return 0
                res = 0
                if node.val == tSum:
                    res += 1
                res += rootSum(node.left,tSum-node.val) 
                res += rootSum(node.right,tSum-node.val)
                return res
    
            if root is None: return 0
            ans = rootSum(root,targetSum)
            ans += self.pathSum(root.left,targetSum)
            ans += self.pathSum(root.right,targetSum)
            return ans
    

    124. 二叉树中的最大路径和(model-VII 路径)

    题目链接:124. 二叉树中的最大路径和
    题目大意:路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

    • 路径和 是路径中各节点值的总和。给你一个二叉树的根节点 root ,返回其 最大路径和 。

    例如:
    在这里插入图片描述

    输入:root = [1,2,3]
    输出:6
    解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
    

    在这里插入图片描述

    输入:root = [-10,9,20,null,null,15,7]
    输出:42
    解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
    
    • 解题思路: DFS 注意 return 以及 P 的求解
    • 时间复杂度: O ( N ) O(N) O(N) N为二叉树的结点数
    • 空间复杂度: O ( N ) O(N) O(N)
    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def __init__(self):
            self.maxN = -float('inf')
    
        def maxPathSum(self, root: Optional[TreeNode]) -> int:
            
            def dfs(node: Optional[TreeNode]) -> int:
                if node is None: return 0
    
                L = max(dfs(node.left),0)
                R = max(dfs(node.right),0)
                P = node.val + L + R 
                self.maxN = max(self.maxN,P)
                return node.val + max(L,R)
    
            dfs(root)
            return self.maxN
    

    总结

       努力 奋斗! 这部分内容那个 主要是和 二叉搜索树、平衡二叉树、二叉树路径问题相关,非常的基础。解决树的问题,DFS 首选项,要多学多思考。

  • 相关阅读:
    LeetCode每日一题——808. 分汤
    scrapy框架选择器
    从输入一个网址到浏览器页面展示到底发生了什么
    c++使用dcmtk读取dicom数据和获取tag值和图像值
    9.nginx代理
    2020最新Java常见面试题及答案
    AI学习指南数学工具篇-PCA的数学原理
    路由器和交换机之间的区别
    consul 备份还原导入导出
    自接触svn之后对git的了解及其应用
  • 原文地址:https://blog.csdn.net/weixin_42269028/article/details/127079834