数据结构与算法——二叉树的题目

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

此题为leetcode第235题
思路:因为此题的前提是二叉搜索树,有个很好的性质即根节点的左子树的值都小于根节点,右子树的值都大于根节点。因此给定两个值p和q,如果p和q都小于根节点的话说明他们一定在左子树,继续在左子树寻找,对于右子树也是如此;如果p和q有一个小于等于根节点而另一个大于等于根节点,说明当前根节点便是最近公共祖先。

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 递归
        # 当p、q的值都小于root时,要在左子树找
        if p.val < root.val and q.val < root.val:
            return self.lowestCommonAncestor(root.left, p, q)
        # 当p、q的值都大于root时,要在右子树找
        if p.val > root.val and q.val > root.val:
            return self.lowestCommonAncestor(root.right, p, q)
        # 否则p或q分列左右子树,root即为最近公共祖先
        return root
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 迭代
        while root:
            if p.val < root.val and q.val < root.val:
                root = root.left
            elif p.val > root.val and q.val > root.val:
                root = root.right
            else:
                return root

二、二叉树的最近公共祖先

此题为leetcode第236题
思路:设left和right分别表示节点x的左右子树是否包含节点p或q,如果包含为True,否则为False。那么符合条件的最近公共祖先节点x满足以下条件:

( l e f t & & r i g h t ) ∥ ( ( x = = p ∥ x = = q ) & & ( l e f t ∥ r i g h t ) ) (left \quad\&\& \quad right) \quad ∥ \quad ((x==p ∥x==q) \quad \&\& \quad (left ∥right)) (left&&right)((x==px==q)&&(leftright))

对于第一个条件,说明左子树和右子树均包含p节点或q节点,如果左子树包含的是p节点,那么右子树只能包含q节点,反之亦然,因为p节点和q节点都是不同且唯一的节点,因此如果满足这个判断条件即可说明x就是我们要找的最近公共祖先。对于第二个条件,即是考虑了x恰好是p节点或q节点且它的左子树或右子树有一个包含了另一个节点的情况,因此如果满足这个判断条件亦可说明x就是我们要找的最近公共祖先。

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def helper(root, p, q):
            if root is None:
                return False
            
            left = helper(root.left, p, q)
            right = helper(root.right, p, q)
            
            if (left and right) or ((root.val == p.val or root.val == q.val) and (left or right)):
                self.res = root
            return left or right or (root.val == p.val or root.val == q.val)
        
        self.res = None
        helper(root, p, q)
        return self.res

三、最深叶节点的最近公共祖先

此题为leetcode第1123题
思路:最深的叶节点可能有多个,如果只有一个最深叶节点的话,那么最近公共祖先就是它自己。我们发现最近公共祖先的左右子树都是等高的,如果不等高,那么高度较小的那个子树的叶节点就不是最深的。我们递归地解决该问题,每次递归返回两个值:一是当前节点子树中最深叶节点的最近公共祖先,另一个是当前节点的深度。然后比较:(1)如果左子树的深度大于右子树的深度,说明最深叶节点在左子树中,那么返回左子树的最深叶节点的最近公共祖先和当前节点的深度;(2)如果左子树的深度小于右子树的深度,说明最深叶节点在右子树中,那么返回右子树的最深叶节点的最近公共祖先和当前节点的深度;(3)如果左右子树的深度相等,那么返回当前节点和当前节点的深度。

class Solution:
    def lcaDeepestLeaves(self, root: TreeNode) -> TreeNode:
        def helper(root):
            if not root:
                return None, 0
            
            left_node, left_depth = helper(root.left)
            right_node, right_depth = helper(root.right)
            
            if left_depth < right_depth:
                return right_node, right_depth + 1
            elif left_depth > right_depth:
                return left_node, left_depth + 1
            else:
                return root, left_depth + 1
            
        return helper(root)[0]

四、二叉树的直径

此题为leetcode第543题
思路:假设我们知道对于该节点的左儿子向下遍历经过最多的节点数L(即以左儿子为根的子树的深度) 和其右儿子向下遍历经过最多的节点数R(即以右儿子为根的子树的深度),那么以该节点为起点的路径经过节点数的最大值即为 L + R + 1。我们记节点node为起点的路径经过节点数的最大值为d,那么二叉树的直径就是所有节点d的最大值减一。

class Solution:
    def diameterOfBinaryTree(self, root: TreeNode) -> int:
        self.res = 1
        self.DFS(root)
        return self.res - 1
        
    def DFS(self, node):
        if node is None:
            return 0
        L = self.DFS(node.left)     # 左子树的深度
        R = self.DFS(node.right)    # 右子树的深度
        self.res = max(self.res, L + R + 1)
        return max(L, R) + 1    # 返回以该节点为子树的深度

五、对称二叉树

此题为leetcode第101题
思路:对于递归,我们递归的比较左子树left和右子树right,如果left和right均为None,那么返回True;如果left和right只有一个为None,那么返回False;如果left的值不等于right的值,那么返回False;然后递归地比较left的左子树和right的右子树,然后比较left的右子树和right的左子树。对于迭代,从队列中取出left和right,如果left和right均为None,那么continue;如果只有一个为None,那么返回False;如果left的值不等于right的值,那么返回False;然后按照下面的顺序依次入队节点:left.left,right.right,left.right,right.left

# 递归
class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if root is None:
            return True

        def helper(left_node, right_node):
            if left_node is None and right_node is None:
                return True
            if left_node is None or right_node is None:
                return False
            if left_node.val != right_node.val:
                return False
            
            return helper(left_node.left, right_node.right) and helper(left_node.right, right_node.left)

        return helper(root.left, root.right)
# 迭代
from collections import deque
class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if root is None:
            return True

        q = deque()
        q.append(root)
        q.append(root)
        while q:
            node1 = q.pop()
            node2 = q.pop()
            if node1 is None and node2 is None:
                continue
            if node1 is None or node2 is None:
                return False
            if node1.val != node2.val:
                return False
            if node1.val == node2.val:
                q.append(node1.left)
                q.append(node2.right)
                q.append(node1.right)
                q.append(node2.left)
        return True

六、翻转二叉树

此题为leetcode第226题
思路:可以用递归或迭代去解决。对于递归,在当前节点root下,我们先递归地返回翻转好的root.left和root.right,并将它们交换,然后返回root。对于迭代,类似于BFS、层序遍历,当前循环下,队列出队一个节点node,交换node.left和node.right,然后依次将node.left和node.right入队。

# 递归
class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if root is None:
            return root

        left = self.mirrorTree(root.left)
        right = self.mirrorTree(root.right)
        
        root.left, root.right = right, left
        
        return root
# 迭代
from collections import deque
class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if root is None:
            return root
        
        q = deque()
        q.append(root)
        while q:
            node = q.popleft()
            node.left, node.right = node.right, node.left
            
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
        return root

七、二叉树的最大深度

此题为leetcode第104题
思路:递归方法:我们可以将当前节点的左右子树分别传入下层递归,并返回他们的最大深度left_depth和right_depth,他们取最大值并加1即为当前节点下的最大深度。迭代:类似于层序遍历,队列入队当前节点和深度,循环的时候依次弹出,和上层循环已经保存的最大深度对比取最大者,然后左右子树及其深度入队。

# 递归
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if root is None:
            return 0
        
        left_depth = self.maxDepth(root.left)       # 左子树深度
        right_depth = self.maxDepth(root.right)     # 右子树深度
        return max(left_depth, right_depth) + 1   
# 迭代
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if root is None:
            return 0
        
        depth = 0
        q = deque()
        q.append((root, 1))
        while q:
            node, curr_depth = q.popleft()
            depth = max(depth, curr_depth)
            
            if node.left:
                q.append((node.left, curr_depth + 1))
            if node.right:
                q.append((node.right, curr_depth + 1))
        return depth

八、二叉树锯齿形层序遍历

此题为leetcode第103题
思路:对于BFS,在每层遍历结束后,按照层是奇数还是偶数,来判断是顺序append还是逆序append。对于DFS来说也是,可以每层设置一个队列,是偶数层就deque.append,是奇数层就deque.appendleft。注意最后将答案里的队列转为列表。

# BFS
from collections import deque
class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        if root is None:
            return []
        
        q = deque()
        q.append(root)
        res = []
        level = 1
        while q:
            size = len(q)
            temp = []
            
            for i in range(size):
                node = q.popleft()
                temp.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
            
            if level % 2 == 1:
                res.append(temp)
            else:
                res.append(temp[::-1])
            level += 1
        return res
# DFS
from collections import deque
class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        def helper(node, level):
            if level >= len(res):
                res.append(deque([node.val]))
            else:
                if level % 2 == 0:
                    res[level].append(node.val)
                else:
                    res[level].appendleft(node.val)
            
            if node.left:
                helper(node.left, level + 1)
            if node.right:
                helper(node.right, level + 1)
            
            
        if root is None:
            return []
        res = []
        helper(root, 0)
        return [list(a) for a in res]

九、另一个树的子树

leetcode第572题
思路:遍历s的每个节点,然后判断s的每个子树是否包含和t具有相同结构和节点的子树。

class Solution:
    def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
        if s is None or t is None:
            return False
        
        stack = [s]
        while stack:
            node = stack.pop()
            if node is not None:
                if node.val == t.val and self.is_equal(node, t):
                    return True
                
                if node.left:
                    stack.append(node.left)
                if node.right:
                    stack.append(node.right)
        return False
    
    def is_equal(self, node1, node2):
        if node2 is None and node1 is not None:
            return False
        if node1 is None and node2 is None:
            return True
        if node1 is None or node1.val != node2.val:
            return False
        
        return self.is_equal(node1.left, node2.left) and self.is_equal(node1.right, node2.right)

十、路径总和

此题为leetcode第112题
思路:递归地判断路径总和是否等于目标值。如果遇到叶子节点,此时路径总和为目标值则返回true,否则返回false

class Solution:
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if root is None:
            return False
        self.sum = sum
        return self.dfs(root, 0)
    
    def dfs(self, root, path):
        if root is None:
            return False
        
        path = path + root.val
        if root.left is None and root.right is None:
            return path == self.sum
    
        return self.dfs(root.left, path) or self.dfs(root.right, path)

十一、路径总和II

此题为leetcode第113题
思路:和上一题类似,在递归的过程中如果遇到等于目标值的路径,就将其放入self.res中。

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        if root is None:
            return []
        self.res = []
        self.sum = sum
        self.dfs(root, 0, [])
        return self.res
    
    def dfs(self, root, curr_sum, path):
        if root is None:
            return None
        
        if root.left is None and root.right is None and curr_sum + root.val == self.sum:
            self.res.append(path + [root.val])
        
        self.dfs(root.left, curr_sum + root.val, path + [root.val])
        self.dfs(root.right, curr_sum + root.val, path + [root.val])

未完待续

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值