Leetcode题解-数据结构-树(python版)

文章目录

    • 1、递归
      • 1.1 树的高度
      • 1.2 平衡树
      • 1.3 归并两棵树
      • 1.4 判断是否存在一条路径和等于一个数
      • 1.5 统计路径和等于某个数的路径总数
      • 1.6 子树
      • 1.7 判断树是否对称
      • 1.8 两节点间的最长路径
      • 1.9 翻转树
      • 1.10 最小路径
      • 1.11 统计左叶子结点的和
      • 1.12 相同节点的最大路径长度
      • 1.13 间隔层序遍历
      • 1.14 二叉树中第二小的结点
      • 1.15 二叉树中第二小的结点
    • 2、层序遍历
      • 2.1 二叉树的层序遍历
      • 2.2 二叉树每层节点的平均值
      • 2.3 找树左下角的结点
      • 2.4 之字形打印二叉树
    • 3、前中后序遍历
      • 3.1 非递归实现二叉树前序遍历
      • 3.2 非递归实现二叉树中序遍历
      • 3.3 非递归实现二叉树后序遍历

1、递归

1.1 树的高度

104. 二叉树的最大深度(Easy)

如果左子树和右子树的最大深度为 l 和 r,那么该二叉树的最大深度即为 max(l,r) + 1,左子树和右子树的最大深度又可以以同样的方式计算

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def maxDepth(self, root):
        if root == None:
            return 0
        return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))

时间复杂度:O(n),n 为二叉树节点的个数。每个节点被遍历一次
空间复杂度:O(height),height 表示二叉树的高度。递归函数需要栈空间,而栈空间取决于递归的深度。

1.2 平衡树

110. 平衡二叉树(Easy)

有了计算节点高度的函数,即可判断二叉树是否平衡。是平衡树的条件是当前左右子树的高度差小于等于 1,且左右子树分别是平衡树

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isBalanced(self, root):
        def Depth(root):
            if root == None:
                return 0
            return 1 + max(Depth(root.left), Depth(root.right))
        
        if root == None:
            return True
        return abs(Depth(root.left) - Depth(root.right)) < 2 and self.isBalanced(root.left) and self.isBalanced(root.right)

1.3 归并两棵树

617. 合并二叉树(Easy)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def mergeTrees(self, t1, t2): 
        if t1 == None:
            return t2
        if t2 == None:
            return t1
        root = TreeNode(t1.val + t2.val)
        root.left = self.mergeTrees(t1.left, t2.left)
        root.right = self.mergeTrees(t1.right, t2.right)
        return root

1.4 判断是否存在一条路径和等于一个数

112. 路径总和(Easy)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def hasPathSum(self, root, sum):
        if root == None:
            return False
        if root.left == None and root.right == None and root.val == sum:
            return True
        return self.hasPathSum(root.left, sum-root.val) or self.hasPathSum(root.right, sum-root.val)

1.5 统计路径和等于某个数的路径总数

437. 路径总和 III(Medium)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def pathSum(self, root, sum):
        def pathSum_from_root(root, sum):
            if root == None:
                return 0
            res = 0
            if root.val == sum:
                res += 1
            res +=  pathSum_from_root(root.left, sum-root.val) + pathSum_from_root(root.right, sum-root.val)
            return res
        
        if root == None:
            return 0
        return self.pathSum(root.left, sum) + self.pathSum(root.right, sum) + pathSum_from_root(root, sum)

1.6 子树

572. 另一个树的子树(Easy)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def isSubtree(self, s, t):
        def issame(s, t):
            if s == None and t == None:
                return True
            if s == None or t == None:
                return False
            if s.val != t.val:
                return False
            return issame(s.left, t.left) and issame(s.right, t.right)
        
        if s == None:
            return False
        return issame(s, t) or self.isSubtree(s.left, t) or self.isSubtree(s.right, t)

1.7 判断树是否对称

101. 对称二叉树(Easy)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isSymmetric(self, root):
        def issame(s, t):
            if s == None and t == None:
                return True
            if s == None or t == None:
                return False
            if s.val != t.val:
                return False
            return issame(s.left, t.right) and issame(s.right, t.left)
        
        if root == None:
            return True
        return issame(root.left, root.right)

1.8 两节点间的最长路径

543. 二叉树的直径(Easy)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def diameterOfBinaryTree(self, root):
        self.res = 0
        def Depth(root):
            if root == None:
                return 0
            L = Depth(root.left)
            R = Depth(root.right)
            self.res = max(self.res, L + R)
            return 1 + max(L, R)
        Depth(root)
        return self.res

1.9 翻转树

226. 翻转二叉树(Easy)
方法一:递归

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def invertTree(self, root):
        if root == None:
            return root
        node = root.left
        root.left = self.invertTree(root.right)
        root.right = self.invertTree(node)
        return root

方法二:迭代
中序遍历的思想,按照二叉树的层次,将每一行节点压入队列中,取出元素时交换左右子树

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def invertTree(self, root):
        if root == None:
            return root
        queue = [root]
        while queue:
            current = queue.pop(0)
            tmp_node = current.left
            current.left = current.right
            current.right = tmp_node
            if current.left:
                queue.append(current.left)
            if current.right:
                queue.append(current.right)
        return root

1.10 最小路径

111. 二叉树的最小深度(Easy)

方法一:深度优先搜索
求树的高度的变形,最小路径等于左子树的最小路径与右子树最小路径的较小值加1。注意:如果有一棵子树为空,只算另外一棵不为空的子树。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def minDepth(self, root):
        if root == None:
            return 0
        L = self.minDepth(root.left)
        R = self.minDepth(root.right)
        if L == 0 or R == 0:
            return 1 + L + R
        return min(L, R)+1

方法二:广度优先搜索

类似与层序遍历,当我们找到一个叶子节点时,直接返回这个叶子节点的深度。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def minDepth(self, root):
        if root == None:
            return 0
        queue = collections.deque([(root, 1)])
        while queue:
            node, depth = queue.popleft()
            if node.left == None and node.right == None:
                return depth
            if node.left:
                queue.append([node.left, depth+1])
            if node.right:
                queue.append([node.right, depth+1])
        return 0

1.11 统计左叶子结点的和

404. 左叶子之和(Easy)

方法一:递归
对于一个节点

  • 如果它的左节点是叶子节点,累加,否则求左子树的左节点之和
  • 求右子树的左节点之和
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def sumOfLeftLeaves(self, root):
        if root == None:
            return 0
        left_leavers_sum = 0
        if root.left:
            if root.left.left == None and root.left.right == None:
                left_leavers_sum += root.left.val
            else:
                left_leavers_sum += self.sumOfLeftLeaves(root.left)
        if root.right:
            left_leavers_sum += self.sumOfLeftLeaves(root.right)
        return left_leavers_sum

方法二:迭代
层序遍历,当节点为左节点且为叶子节点时,进行累加
将节点放入队列时,左节点标记为 1,右节点标记为 0

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def sumOfLeftLeaves(self, root):
        left_leavers_sum = 0
        if root == None or (root.left == None and root.right == None):
            return 0
        queue = collections.deque([(root, 1)])
        while queue:
            current, isleft = queue.popleft()
            if current.left == None and current.right == None and isleft:
                left_leavers_sum += current.val
            if current.left:
                queue.append([current.left, 1])
            if current.right:
                queue.append([current.right, 0])
        return left_leavers_sum

1.12 相同节点的最大路径长度

687. 最长同值路径(Easy)

在递归函数中,首先对其左右子结点调用递归函数,得到其左右子树的最大相同值路径长度,接下来看当前结点和其左右子结点之间的关系了,如果其左子结点存在且和当前节点值相同,则左侧长度加 1,否则以当前节点为根节点的左侧相同节点的最大路径长度为 0;同理,如果其右子结点存在且和当前节点值相同,右侧长度加 1,否则为0。若该结点最长相同路径 left_arrow + right_arrow 大于 res,更新 res。

调用当前节点值的函数返回 left_arrow 和 right_arrow 中的较大值,原因如下:递归函数返回的是以该结点为终点的最长路径长度,这样回溯时,还可以继续连上其父结点。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def longestUnivaluePath(self, root):
        self.res = 0

        def arrow_len(root):
            if root == None:
                return 0
            left_length = arrow_len(root.left)
            right_length = arrow_len(root.right)
            left_arrow, right_arrow = 0, 0
            if root.left and root.left.val == root.val:
                left_arrow = left_length + 1
            if root.right and root.right.val == root.val:
                right_arrow = right_length + 1
            self.res = max(self.res, left_arrow + right_arrow)
            return max(left_arrow, right_arrow)
        
        arrow_len(root)
        return self.res

1.13 间隔层序遍历

337. 打家劫舍 III(Medium)

将问题分解为偷根节点与不偷根节点,取二者最大值。偷根节点时,总钱数等于根节点钱数加上左右子树均不偷根节点钱数之和。
不偷根节点时,左右两颗子树可以偷根节点,也可以不偷,取两颗子树的最大值,加和。

left_rob 表示偷左子节点,left_no 表示不偷左子节点
right_rob 表示偷右子节点,right_no 表示不偷右子节点

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def rob(self, root):
        def _rob(root):
            if root == None: return 0, 0
            left_rob, left_no = _rob(root.left)
            right_rob, right_no = _rob(root.right)
            return root.val + left_no + right_no, max(left_rob, left_no) + max(right_rob, right_no)

        return max(_rob(root))

1.14 二叉树中第二小的结点

671. 二叉树中第二小的节点(Easy)

如果根节点有子节点,则将左右结点的值保存,如果左节点值等于根节点的值,说明一定不是第二小的结点值,以左子节点为根节点寻找第二小的结点值(递归调用)。右侧也相同。在左右子树找到的第二小结点后进行比较,若都不为-1,返回较小的那个,若其中一个为-1,返回另外一个,否则不存在,返回-1。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def findSecondMinimumValue(self, root):
        if root == None:
            return -1
        if root.left == None and root.right == None:
            return -1
        leftval = root.left.val
        rightval = root.right.val
        if leftval == root.val:
            leftval = self.findSecondMinimumValue(root.left)
        if rightval == root.val:
            rightval = self.findSecondMinimumValue(root.right)
        if leftval != -1 and rightval != -1:
            return min(leftval, rightval)
        if leftval != -1:
            return leftval
        return rightval

方法二:遍历,求非根节点值之外的最小值

也可以直接遍历,求非根节点值之外的最小值,这里我用了中序遍历,每遍历一层,将非根节点值保存,挑出最小值,如果遍历之后该值存在,则返回,不存在返回 -1

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def findSecondMinimumValue(self, root):
        queue = collections.deque()
        queue.append(root)
        res = float('inf')
        while queue:
            big = []
            queue_len = len(queue)
            for _ in range(queue_len):
                current = queue.popleft()
                if current.val > root.val:
                    big.append(current.val)
                if current.left:
                    queue.append(current.left)
                    queue.append(current.right)
            if big:
                big = sorted(big)
                res = min(res, big[0])
        return -1 if res == float('inf') else res

也可以使用前序遍历,保存根节点的值为 small,第二小的数定义为 second,初始化为 -1,如果第一次遇见不等于根节点的数值,直接赋值给 second,不是第一次遇见不等于根节点的数值,如果比 second 小,更新 second

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def findSecondMinimumValue(self, root):
        self.second = -1
        self.small = root.val
        def preview(root):
            if root == None:
                return
            if self.second == -1 and root.val != self.small:
                self.second = root.val
            if self.second != -1 and root.val != self.small:
                self.second = min(root.val, self.second)
            preview(root.left)
            preview(root.right)
        
        if root == None:
            return -1
        preview(root)
        return self.second

1.15 二叉树中第二小的结点

894. 所有可能的满二叉树(Medium)
方法:递归

令 f(N) 作为所有含 N 个结点的可能的满二叉树的列表。

每个满二叉树含有 3 个或更多结点,在其根结点处有 2 个子结点。这些子结点 left 和 right 本身就是满二叉树。因此,对于 N≥3,我们可以设定如下的递归策略:f(N) = [对于所有的 x,所有的树的左子结点来自 f(x) 而右子结点来自 f(N−1−x)]。

最后,我们应该缓存函数 f(N) 之前的结果,这样我们就不必在递归中重新计算它们。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    memo = {
     0:[], 1:[TreeNode(0)]}
    def allPossibleFBT(self, N):
        if N in self.memo:
            return self.memo[N]
        res = []
        for i in range(1, N, 2):
            for left in self.allPossibleFBT(i):
                for right in self.allPossibleFBT(N-i-1):
                    root = TreeNode(0)
                    root.left = left
                    root.right = right
                    res.append(root)
        return res
        """
        :type N: int
        :rtype: List[TreeNode]
        """

2、层序遍历

2.1 二叉树的层序遍历

102. 二叉树的层序遍历(Medium)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        ans = []
        if not root:
            return ans
        queue = [root]
        while queue:
            n = len(queue)
            level = []
            for _ in range(n):
                child = queue.pop(0)
                level.append(child.val)
                if child.left:
                    queue.append(child.left)
                if child.right:
                    queue.append(child.right)
            ans.append(level)
        return ans

2.2 二叉树每层节点的平均值

637. 二叉树的层平均值(Easy)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def averageOfLevels(self, root):
        res = list()
        queue = collections.deque()
        queue.append(root)
        while queue:
            len_queue = len(queue)
            sum = 0.0
            for _ in range(len_queue):
                current = queue.popleft()
                sum += current.val
                if current.left:
                    queue.append(current.left)
                if current.right:
                    queue.append(current.right)
            res.append(sum/len_queue)
        return res

2.3 找树左下角的结点

513. 找树左下角的值(Easy)

从右到左的层序遍历,访问的最后一个元素就是左下角的节点

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def findBottomLeftValue(self, root):
        queue = collections.deque()
        queue.append(root)
        while queue:
            cur = queue.popleft()
            if cur.right:
                queue.append(cur.right)
            if cur.left:
                queue.append(cur.left)
        return cur.val

2.4 之字形打印二叉树

103. 二叉树的锯齿形层次遍历(Medium)

用两个栈分别保存从左向右和从右向左的元素。栈中每次保存二叉树一层的数据。
第 1,3,5 等奇数行的数据从左向右依次入栈,2,4,6 偶数行的数据从右向左依次入栈,后入栈的先出栈

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    
    def preorderTraversal(self, root):
        res = list()
        stack = []
        p = root
        while(p or stack):
            while(p):
                res.append(p.val)
                stack.append(p)
                p = p.left
            p = stack.pop()
            p = p.right
        return res

时间复杂度:O(n),其中 n 是二叉树的节点数,每一个节点恰好被遍历一次。
空间复杂度:O(n),为迭代过程中栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)

3、前中后序遍历

3.1 非递归实现二叉树前序遍历

144. 二叉树的前序遍历(Medium)
问题分析:
先序、中序和后序遍历过程:遍历过程中经过结点的路线一样,只是访问各结点的时机不同。
Leetcode题解-数据结构-树(python版)_第1张图片

# 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 preorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        stack = []
        p = root
        while(p != None or len(stack) != 0):
            if p != None:
                res.append(p.val)
                stack.append(p)
                p = p.left
            else:
                top = stack.pop()
                p = top.right
        return res

3.2 非递归实现二叉树中序遍历

94. 二叉树的中序遍历(Medium)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def inorderTraversal(self, root):
        res = list()
        stack = []
        p = root
        while(p or stack):
            while p:
                stack.append(p)
                p = p.left
            p = stack.pop()
            res.append(p.val)
            p = p.right
        return res
        """
        :type root: TreeNode
        :rtype: List[int]
        """

3.3 非递归实现二叉树后序遍历

145. Binary Tree Postorder Traversal(Hard)

问题分析:

要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

# 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 postorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        if root == None:
            return res
        
        stack = []
        stack.append(root)
        cur = None
        pre = None
        while stack:
            cur = stack[-1]
            if (cur.left == None and cur.right == None) or ((cur.left == pre or cur.right == pre) and pre != None):
                res.append(cur.val)
                pre = cur
                stack.pop()
            else:
                if cur.right:
                    stack.append(cur.right)
                if cur.left:
                    stack.append(cur.left)
        return res

你可能感兴趣的:(leetcode题解)