leetcode专题刷题记录I——二叉树

按照一位b站up主的刷题推荐顺序进行学习
每道题的题解代码有的参考他的题解或者leetcode评论区题解

文章目录

    • 144 二叉树前序遍历
    • 94 二叉树中序遍历
    • 145 二叉树的后序遍历=根右左的逆序
    • 173 二叉树迭代器
    • 102 二叉树的层序遍历
    • 103 二叉树的锯齿形遍历
    • 107 二叉树的层序遍历II
    • 98 验证二叉搜索树
    • 99 恢复二叉搜索树
    • 230 二叉搜索树最小的第K个元素
    • 257 二叉树的所有路径
    • 199 二叉树的右视图
    • 198 打家劫舍
    • 213 打家劫舍II
    • 337 打家劫舍III
    • 114 二叉树展开为链表
    • 116 填充每个节点的下一个右侧节点指针
    • 117 填充每个节点的下一个右侧节点指针II(难)
    • 297 二叉树的序列化和反序列化
    • 449 二叉树搜索树的序列化和反序列化
    • 95 不同的二叉搜索树II
    • 96 不同的二叉搜索树
    • 124 二叉树中的最大路径和
    • 235 二叉搜索树的最近公共祖先
    • 236 二叉树的最近公共祖先
    • 100 相同的树
    • 104 二叉树的最大深度
    • 105 从前序和中序遍历序列构造二叉树
    • 106 从中序和后续遍历序列构造二叉树
    • 108 将有序数组转换为平衡二叉搜索树
    • 109 有序链表转换二叉搜索树
    • 110 平衡二叉树
    • 111 二叉树的最小深度
    • 112 路径总和
    • 113 路径总和II
    • 129 求根节点到叶节点的路径和
    • 222 完全二叉树
    • 226 翻转二叉树
    • 101 对称二叉树

144 二叉树前序遍历

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        stack = []
        while root or stack != []:
            while root:
                res.append(root.val)
                stack.append(root)
                root = root.left

            cur = stack.pop(-1)
            root = cur.right 
        
        return res

94 二叉树中序遍历

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        ans = []
        stack = []
        while root or stack != []:
            while root:
                stack.append(root)
                root = root.left

            cur = stack.pop()
            ans.append(cur.val)
            root = cur.right

        return ans

145 二叉树的后序遍历=根右左的逆序

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        stack = []
        while root or stack != []:
            while root:
                res.append(root.val)
                stack.append(root)
                root = root.right
            
            cur = stack.pop()
            root = cur.left
        
        return res[::-1]

173 二叉树迭代器

class BSTIterator:
    def __init__(self, root: TreeNode):
        self.list = []
        self.progress = 0
        # 得到中序遍历结果
        self.inOrder(root)
        
    def inOrder(self, root):
        stack = []
        while root or stack != []:
            while root:
                stack.append(root)
                root = root.left

            cur =  stack.pop(-1)
            self.list.append(cur.val)
            root = cur.right

    def next(self) -> int:
        res = self.list[self.progress]
        self.progress += 1
        return res


    def hasNext(self) -> bool:
        return self.progress != len(self.list)

102 二叉树的层序遍历

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        queue = [root]
        ans = []
        while queue:
            # 每次for循环遍历处理一层
            # tmp_list用来临时存储下一层的节点
            tmp_list = []
            layer_num = len(queue)
            for i in range(layer_num):
                node = queue.pop(0)
                # 把下一层节点从左到右加入队列
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
                tmp_list.append(node.val)
            ans.append(tmp_list)
        
        return ans

103 二叉树的锯齿形遍历

class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:

        stack = []
        def levelOrder(root, depth, stack):
            if not root: return

            if depth >= len(stack):
                stack.append([])

            stack[depth].append(root.val)
            
            levelOrder(root.left, depth + 1, stack)
            levelOrder(root.right, depth + 1, stack)
        # 先层序遍历
        levelOrder(root, 0, stack)
        # 再根据层数逆序
        for i in range(1, len(stack), 2):
            stack[i] = stack[i][::-1]
        
        return stack

107 二叉树的层序遍历II

class Solution:
    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
        ans = []
        def leverOrder(root, depth, stack):
            if not root: return
            if depth >= len(stack):
                stack.append([])
            
            stack[depth].append(root.val)
            leverOrder(root.left, depth + 1, stack)
            leverOrder(root.right, depth + 1, stack)
        
        # 层序遍历逆序
        leverOrder(root, 0, ans)
        return ans[::-1]

98 验证二叉搜索树

class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        # 先序遍历二叉树,对每个节点判断是否符合取值范围
        def check(root, l, r):
            if not root:
                return True
            if root.val <= l or root.val >= r:
                return False
     		# 限定左右节点的取值范围
            return check(root.left, l, root.val) and check(root.right, root.val, r)
        import math
        return check(root, -math.inf, math.inf)

99 恢复二叉搜索树

class Solution:
    def recoverTree(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        # 中序遍历找到errorOne和errorTwo,交换
        stack = []
        pre = None
        errOne = None
        errTwo = None
        while root or stack != []:
            while root:
                stack.append(root)
                root = root.left
            
            root = stack.pop()
            # 定位errOne, errTwo
            if pre and pre.val >= root.val:
                if not errOne:
                    errOne = pre
                    errTwo = root
                else:
                    errTwo = root
            
            pre = root
            root = root.right

        # 交换errOne, errTwo
        tmp = errOne.val
        errOne.val = errTwo.val
        errTwo.val = tmp

        return root

230 二叉搜索树最小的第K个元素

class Solution:
    def kthSmallest(self, root: TreeNode, k: int) -> int:
        # 中序遍历到第k个值时返回
        stack = []
        n = 0
        while root or stack != []:
            while root:
                stack.append(root)
                root = root.left
            
            cur = stack.pop()
            n += 1
            if n == k: return cur.val
            root = cur.right
        
        return 

285/272锁住了

257 二叉树的所有路径

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        # 递归,根据每个节点的情况判断是否加入路径
        if not root:
            return []
        if not root.left ans not root.right:
            return [str(root.val)]
        
        paths = []
        if root.left:
            for i in self.binaryTreePaths(root.left):
                paths.append(str(root.val) + '->' + i)
        if root.left:
            for i in self.binaryTreePaths(root.right):
                paths.append(str(root.val) + '->' + i)
        
        return paths

270 锁了

199 二叉树的右视图

class Solution:
    def rightSideView(self, root: TreeNode) -> List[int]:
        '''
        # 取层序遍历中每一层的最后一个节点
        stack = []
        def levelOrder(root, depth, stack):
            if not root: return
            if depth >= len(stack):
                stack.append([])
            stack[depth].append(root.val)
            levelOrder(root.left, depth + 1, stack)
            levelOrder(root.right, depth + 1, stack)
        levelOrder(root, 0, stack)
        ans = []
        for l in stack:
            ans.append(l[-1])
        return ans
        '''
        # 改进空间复杂度 层序遍历按照从右到左的顺序
        ans = []
        def dfs(root, depth):
            if not root: return
            if depth == len(ans):
                ans.append(root.val)
            dfs(root.right, depth + 1)
            dfs(root.left, depth + 1)

        dfs(root, 0)
        return ans

198 打家劫舍

class Solution:
    def rob(self, nums: List[int]) -> int:
        nums_len = len(nums)
        if nums_len <= 1:
            return 0 if nums_len == 0 else nums[0]

        # dp[i] 代表截止到目前这一家,我们能够拿到的最大钱数

        dp = [0] * nums_len
        res = max(nums[0], nums[1])
        for i in range(nums_len):
            if i== 0:
                dp[i] = nums[0]
            elif i == 1:
                dp[i] = max(nums[0], nums[1])
            else:
                dp[i] = max(dp[i-1], dp[i-2] + nums[i])
            
            res = max(res, dp[i])
        
        return res

213 打家劫舍II

class Solution:
    def rob(self, nums: List[int]) -> int:
        # 分类处理避免头尾成环的情况
        nums_len = len(nums)
        if nums_len <= 1:
            return 0 if nums_len == 0 else nums[0]
        
        nums1 = nums[:-1]
        nums2 = nums[1:-1]
        nums3 = nums[1:]
		# 这里直接复用198的代码
        return max(self.helper(nums1), self.helper(nums2), self.helper(nums3))
    
    def helper(self, nums):
        nums_len = len(nums)
        if nums_len <= 1:
            return 0 if nums_len == 0 else nums[0]

        dp = [0] * nums_len
        res = max(nums[0], nums[1])
        for i in range(nums_len):
            if i == 0:
                dp[i] = nums[0]
            elif i == 1:
                dp[i] = max(nums[0], nums[1])
            else:
                dp[i] = max(dp[i-1], dp[i-2] + nums[i])
            
            res = max(res, dp[i])

        return res

337 打家劫舍III

class Solution:
    def rob(self, root: TreeNode) -> int:
        def dp(root):
            if not root: return [0,0]

            left = dp(root.left)
            right = dp(root.right)

            rob = root.val + left[1] + right[1]
            not_rob = max(left[0], left[1]) + max(right[0], right[1])

            return [rob, not_rob]

        res = dp(root)
		# 每个节点能够盗取的最大金额,分类为抢劫和不抢劫当前节点
        # 0代表抢劫当前节点, 1代表不抢劫当前节点
        return max(res[0], res[1])

255 锁了

114 二叉树展开为链表

class Solution:
    def flatten(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        # 先序遍历
        if not root:
            return
        
        # 先展平左节点,再展平右节点
        left = self.flatten(root.left)
        right = self.flatten(root.right)

        # 再把左节点放到根节点的右边
        root.left = None
        root.right = left

        # 再把原来的右节点放到现在的最右边
        p = root
        while p.right:
            p = p.right
        p.right = right

        return root

156 锁了

116 填充每个节点的下一个右侧节点指针

class Solution:
    def connect(self, root: 'Node') -> 'Node':
        if not root:return

        # 利用辅助函数,定义每两个相邻的节点需要完成的任务
        def helper(node1, node2):
            if not node1:
                return
            
            # 和自己相邻的节点连起来
            node1.next = node2
            # 递归自己的子节点
            helper(node1.left, node1.right)
            helper(node2.left, node2.right)
            # 递归自己和相邻节点的子节点
            helper(node1.right, node2.left)

        helper(root.left, root.right)
        return root

117 填充每个节点的下一个右侧节点指针II(难)

class Solution:
    def connect(self, root: 'Node') -> 'Node':
        # left_one代表左边第一个
        left_one = root
        # 逐层遍历
        while left_one:
            # 用head作为连接后的下一层的虚拟头节点
            head = tail = Node(0)
            # cur从这一层左边第一个开始
            cur = left_one
            while cur:
                # 利用tail不断移动,连接这一层节点的下一层
                if cur.left:
                    tail.next = cur.left
                    tail = tail.next
                if cur.right:
                    tail.next = cur.right
                    tail = tail.next
                cur = cur.next
            left_one = head.next
        
        return root

297 二叉树的序列化和反序列化

使用层序序列

class Codec:
    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        if not root: return ''

        res = []
        queue = [root]
        while queue:
            len_q = len(queue)
            for i in range(len_q):
                cur = queue.pop(0)
                res.append('#' if not cur else str(cur.val))

                if not cur: continue

                queue.append(cur.left)
                queue.append(cur.right)
        
        return ','.join(res)

        
    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        if data == '': return

        nodes = data.split(',')
        root = TreeNode(int(nodes[0]))

        queue = [root]
        len_nodes = len(nodes)
        i = 1
        while i < len_nodes:
            parent = queue.pop(0)

            if nodes[i] != '#':
                parent.left = TreeNode(int(nodes[i]))
                queue.append(parent.left)
            if nodes[i+1] != '#':
                parent.right = TreeNode(nodes[i+1])
                queue.append(parent.right)
            
            i += 2
        
        return root

449 二叉树搜索树的序列化和反序列化

和279一致即可

95 不同的二叉搜索树II

class Solution:
    def generateTrees(self, n: int) -> List[TreeNode]:
        # 将(1, i-1)看作左子树,(i+1, n)看作右子树

        def helper(start, end):
            res = []
            if start > end:
                res.append(None)
            for i in range(start, end + 1):
                leftChild = helper(start, i - 1)
                rightChild = helper(i + 1,  end)
                # 排列组合
                for left in leftChild:
                    for right in rightChild:
                        root = TreeNode(i)
                        root.left = left
                        root.right = right
                        res.append(root)
            return res
        
        if n == 0: return []
        return helper(1, n)

96 不同的二叉搜索树

class Solution:
    def numTrees(self, n: int) -> int:
        # 动态规划
        # 假设1-n个节点中选择i节点作为根节点
        # 则构成的二叉搜索树数量=以i节点左边的节点构成的二叉搜索树的数量*i节点右边的节点构成的二叉搜索树数量
        # G[n]代表n个节点可以构成的二叉搜索树之和,

        G = [0]*(n + 1)
        G[0] = 1
        G[1] = 1

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

124 二叉树中的最大路径和

class Solution:
    def maxPathSum(self, root: TreeNode) -> int:
        # 后序遍历找最大路径和
        maxRes = - math.inf

        def postOrder(root):
            nonlocal maxRes
            if not root:
                return 0
            
            left = max(0, postOrder(root.left))
            right = max(0, postOrder(root.right))
            
            maxRes = max(maxRes, root.val + left + right)

            return root.val + max(left, right)
        
        postOrder(root)

        return maxRes

235 二叉搜索树的最近公共祖先

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if p.val == q.val: return p

        if p.val < root.val and q.val < root.val:
            # 都在左边
            return self.lowestCommonAncestor(root.left, p, q)
        elif p.val > root.val and q.val > root.val:
            # 都在右边
            return self.lowestCommonAncestor(root.right, p, q)
        else:
            # 左右各一个
            return root

236 二叉树的最近公共祖先

思路:从根节点分别递归遍历自己的左右子树,有三种情况

  1. 根节点=其中一个节点,那么直接返回这个根节点,一定是最近祖先;
  2. 这两个节点分别位于根节点的左右子树,那么这个根节点也是最近祖先;
  3. 这两个节点都位于根节点的其中一棵子树,则对这棵子树进行递归
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 如果已经遍历到底都找不到p或者q就返回None,如果已经遍历到p或者q,则返回这个
        if not root or p == root or q == root: return root

		# 假设p和q都在左子树,返回左子树中的公共节点(None或者找到了其中一个节点)
        pqInleft = self.lowestCommonAncestor(root.left, p, q)
        # 同理
        pqInright = self.lowestCommonAncestor(root.right, p, q)

		# 其中一个子树没找到,则结果一定在另外一个子树中
        if not pqInleft:
            return pqInright
        elif not pqInright:
            return pqInleft
     
     	# 左右子树都没找到,说明只有root=p=q
        return root

250、333锁了

100 相同的树

class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if not p and not q: return True
        if not p or not q: return False
        if p.val != q.val:
            return False
        left = self.isSameTree(p.left, q.left)
        right = self.isSameTree(p.right, q.right)
        return left and right

104 二叉树的最大深度

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1

105 从前序和中序遍历序列构造二叉树

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if not preorder or not inorder:
            return
        # 1.从前序和中序中找到当前节点的值和左右子节点的前序和中序
        # 当前节点的值=前序数组第一个
        cur_value = preorder[0]
        root = TreeNode(cur_value)
        # 找到当前节点的值在中序数组中的位置,前面的就是左节点的中序数组,后面的就是右节点的中序数组
        i = 0
        while cur_value != inorder[i]:
            i += 1
        inorder_mid = i
        # 根据左节点的前序数组长度等于左节点的中序数组长度找到左右节点的前序数组

        # 2.建立左右子节点
        root.left = self.buildTree(preorder[1:inorder_mid+1], inorder[:inorder_mid])
        root.right = self.buildTree(preorder[inorder_mid+1:], inorder[inorder_mid+1:])

        return root

106 从中序和后续遍历序列构造二叉树

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        if not inorder or not postorder:
            return 

        # 1.根据中序和后序找到左右子树的中序和后序
        # 后序的最后一个元素代表当前节点的值
        cur_value = postorder[-1]
        root = TreeNode(cur_value)
        # 找到当前值在中序数组的位置
        i = 0
        while cur_value != inorder[i]:
            i += 1
        inorder_mid = i
        
        # 2.建立左右子树
        root.left = self.buildTree(inorder[:inorder_mid], postorder[:inorder_mid])
        root.right = self.buildTree(inorder[inorder_mid+1:], postorder[inorder_mid:-1])

        return root

108 将有序数组转换为平衡二叉搜索树

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if not nums: return

        nums_len = len(nums)
        mid = nums_len//2

        root = TreeNode(nums[mid])
        root.left = self.sortedArrayToBST(nums[: mid])
        root.right = self.sortedArrayToBST(nums[mid + 1:])

        return root

109 有序链表转换二叉搜索树

class Solution:
    def sortedListToBST(self, head: ListNode) -> TreeNode:
        if not head: return
        elif not head.next: return TreeNode(head.val)
        # 用快慢指针找到中点
        slow = head
        mid = slow.next
        fast = mid.next
        while fast and fast.next:
            slow = slow.next
            mid = slow.next
            fast = fast.next.next
        
        slow.next = None
        root = TreeNode(mid.val)
        root.left = self.sortedListToBST(head)
        root.right = self.sortedListToBST(mid.next)

        return root

110 平衡二叉树

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if not root: return True

        def dfs(root):
            if not root: return 0
            return max(dfs(root.left), dfs(root.right)) + 1
        
        left = dfs(root.left)
        right = dfs(root.right)

        if abs(left - right) > 1:
            return False
        
        return self.isBalanced(root.left) and self.isBalanced(root.right)

111 二叉树的最小深度

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        # 排除非叶子节点的路径
        if not root.left and not root.right:
            return 1
        if not root.left:
            return 1 + self.minDepth(root.right)
        elif not root.right:
            return 1 + self.minDepth(root.left)
        else:
            return min(self.minDepth(root.left), self.minDepth(root.right)) + 1

112 路径总和

class Solution:
    def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
        if not root: return False
		
		# 排除只是根节点的路径
        if root.val == targetSum and not root.left and not root.right:
            return True
            
        targetSum = targetSum - root.val
        return self.hasPathSum(root.left, targetSum) or self.hasPathSum(root.right, targetSum)

113 路径总和II

class Solution:
    def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
        ans = []

        def helper(root, sum, res):
            nonlocal ans
            if not root: return
            sum -= root.val
            if sum == 0 and not root.left and not root.right:
                ans.append(res + [root.val])

            helper(root.left, sum, res+[root.val])
            helper(root.right, sum, res+[root.val])
        
        helper(root, targetSum, [])
        return ans

129 求根节点到叶节点的路径和

class Solution:
    def sumNumbers(self, root: TreeNode) -> int:
        res = []

        def dfs(root, tmp):
            nonlocal res
            if not root:return

            if not root.left and not root.right:
                res.append(tmp + str(root.val))
                return

            dfs(root.left, tmp + str(root.val))
            dfs(root.right, tmp + str(root.val))
        
        dfs(root, '')
        ans = 0
        for arr in res:
            ans += int(arr)
        
        return ans

222 完全二叉树

class Solution:
    def countNodes(self, root: TreeNode) -> int:
        if not root: return 0

        def getDepth(root):
            return 1 + getDepth(root.left) if root else -1
        
        res = 0
        h = getDepth(root)
        while root:
            if getDepth(root.right) == h - 1:
                # 如果右子树的高度等于h-1,则说明左子树是完全二叉树
                # 加上左子树的节点
                res += 1 << h
                # 遍历右子树
                root = root.right
            else:
                # 如果右子树的高度小于h-1,说明此时右子树一定是一个完全二叉树
                # 加上右子树的节点
                res += 1 << (h - 1)
                # 接着遍历左子树
                root = root.left
            h -= 1
        return res

226 翻转二叉树

class Solution:
    def invertTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return 
        # 后续遍历翻转左右节点
        self.invertTree(root.left)
        self.invertTree(root.right)

        tmp = root.left
        root.left = root.right
        root.right = tmp

        return root

298、366锁了

101 对称二叉树

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if not root: return False
        
        def helper(left, right):
            if not left and not right:
                return True
            
            if not left or not right or left.val != right.val:
                return False
            
            return helper(left.left, right.right) and helper(left.right, right.left)

        return helper(root.left, root.right)

你可能感兴趣的:(技能)