【Leetcode】二叉树问题整理笔记



二叉树节点类实现

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


100. Same Tree - 判断给定两棵二叉树是否“相等”

n/a
n/a
n/a
n/a
Solution:

可以使用递归实现。能用递归解决的问题,实现起来会简单很多!

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



按层遍历算法问题

简单的队列实现

class DQ:
    def __init__(self, val):
        self._dq = [val]

    def pop_left(self):
        return self._dq.pop(0)

    def append(self, val):
        self._dq.append(val)
    
    def is_empty(self):
        return len(self._dq) == 0
    
    def __len__(self):
        return len(self._dq)

101. Symmetric Tree - 判断二叉树是否对称

n/a

Solution:

提示有递归的实现方法。确实没有想到。这里是迭代的方式。

class Solution:
    def is_symmetric_list(self, node_list):
        _len = len(node_list)
        if _len % 2 == 1:
            return False
        else:
            half = _len // 2
            for i in range(half):
                if node_list[i] is None and node_list[-i-1] is None:
                    pass
                elif node_list[i] is not None and node_list[-i-1] is not None:
                    if node_list[i].val == node_list[-i-1].val:
                        pass
                    else:
                        return False
                else:
                    return False
        return True

    def is_symmetric(self, root):
        if root is None:
            return True

        dq = DQ(root)
        node = dq.pop_left()
        dq.append(node.left)
        dq.append(node.right)
        while not dq.is_empty():
            cmp = list()
            for _ in range(len(dq)):
                node = dq.pop_left()
                cmp.append(node)
            if not self.is_symmetric_list(cmp):
                return False
            for node in cmp:
                if node is not None:
                    dq.append(node.left)
                    dq.append(node.right)

        return True
        
    def isSymmetric(self, root: TreeNode) -> bool:
        return self.is_symmetric(root)


104. Maximum Depth of Binary Tree - 求二叉树的最大深度

n/a

Solution:

这里的代码和 101 实际上非常相似。只不过不需要判断当前层的值是否是对称的,以及不将为 None 的子树加入队列。

class Solution:
    def get_max_deepth(self, root):
        dq = DQ(root)
        deepth = 0
        while not dq.is_empty():
            deepth += 1
            level = list()
            for _ in range(len(dq)):
                level.append(dq.pop_left())
            
            for node in level:
                if node is not None:
                    if node.left is not None:
                        dq.append(node.left)
                    if node.right is not None:
                        dq.append(node.right)
        return deepth

    def maxDepth(self, root: TreeNode) -> int:
        if root is None:
            return 0
        return self.get_max_deepth(root)

107. Binary Tree Level Order Traversal II - 二叉树按层逆遍历

n/a

Solution:

当然了,这个问题按层遍历的意图更加明显。

不过原来我倒是不大想得到按层遍历除了使用队列之外,还能如何使用递归实现。
但是在考虑这个问题的时候,因为结果需要从低到顶。所以我下意识先考虑了如何使用递归实现。
然后发现其实使用递归在 Python 中还是很好实现的。
当我在考虑,每一层的节点数量不同,难道还需要构造链表吗?
但是在 Python 中,因为非常动态化的可变参数特性,只需将每一层的所有节点“打包”成元组作为一个参数传下(递归调用)(或者在传递的时候拆包,函数定义上使用 *args)就可以。
想通了这一点,递归的实现方式自然也不难。

这里还是使用队列的按层遍历实现:

class Solution:
    def convert_level_order_bottom(self, root):
        dq = DQ(root)
        ret = []
        while not dq.is_empty():
            level = []
            for _ in range(len(dq)):
                node = dq.pop_left()
                if node is not None:
                    level.append(node)
            ret.append([_.val for _ in level])
            for node in level:
                if node.left is not None:
                    dq.append(node.left)
                if node.right is not None:
                    dq.append(node.right)

        return ret[::-1]

    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
        if root is None:
            return []
        return self.convert_level_order_bottom(root)


199. Binary Tree Right Side View - 获取二叉树的所有最右节点

n/a

Solution:

这个问题最一开始还没有想到需要用按层遍历来实现。
主要是这个图不够有代表性,误导很大。

因为这个问题又发掘了 Leetcode 的一个不错的功能,可以可视化二叉树形状:

n/a
总的来说,最终确认了可以按层遍历算法解决之后,其实代码和上面的几个问题都非常相似。
唯一的区别不过就是将一层中的所有节点读取到一个 level 列表之后用来做什么的区别。

class Solution:
    def rightSideView(self, root: TreeNode) -> List[int]:
        if root is None:
            return []

        dq = DQ(root)
        ret = []
        while not dq.is_empty():
            level = list()
            for _ in range(len(dq)):
                level.append(dq.pop_left())
            ret.append(level[-1].val)
            for node in level:
                if node is not None:
                    if node.left is not None:
                        dq.append(node.left)
                    if node.right is not None:
                        dq.append(node.right)
                else:
                    raise RuntimeError("Shouldn't run here")

        return ret


102. Binary Tree Level Order Traversal - 二叉树按层遍历

n/a

Solution:

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if root is None:
            return []
        
        ret = []
        dq = DQ(root)

        node = dq.pop_left()
        level_vals = [node.val]
        ret.append(level_vals)

        dq.append(node.left)
        dq.append(node.right)
        while not dq.is_empty():
            level = list()
            for _ in range(len(dq)):
                node = dq.pop_left()
                if node:
                    level.append(node)

            level_vals = []
            for node in level:
                if node is not None:
                    level_vals.append(node.val)
                    dq.append(node.left)
                    dq.append(node.right)
            if level_vals:
                ret.append(level_vals)
        
        return ret

429. N-ary Tree Level Order Traversal - N-ary 树按层遍历

n/a
n/a

Solution:

仍然使用了队列,代码和 102. Binary Tree Level Order Traversal 几乎完全一样。
只不过对 node.left, node.right 换成了 for child in node.children

"""
# Definition for a Node.
class Node:
    def __init__(self, val, children):
        self.val = val
        self.children = children
"""

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if root is None:
            return []
        
        ret = []
        dq = DQ(root)

        node = dq.pop_left()
        level_vals = [node.val]
        ret.append(level_vals)

        for child in node.children:
            dq.append(child)

        while not dq.is_empty():
            level = list()
            for _ in range(len(dq)):
                node = dq.pop_left()
                if node:
                    level.append(node)

            level_vals = []
            for node in level:
                if node is not None:
                    level_vals.append(node.val)
                    for child in node.children:
                        dq.append(child)

            if level_vals:
                ret.append(level_vals)
        
        return ret

n/a


559. Maximum Depth of N-ary Tree

n/a

Solution:

class Solution:
    def get_max_depth(self, root):
        dq = DQ(root)
        deepth = 0
        while not dq.is_empty():
            deepth += 1
            level = list()
            for _ in range(len(dq)):
                level.append(dq.pop_left())
            
            for node in level:
                if node is not None:
                    for child in node.children:
                        dq.append(child)

        return deepth
    
    def maxDepth(self, root: 'Node') -> int:
        if root is None:
            return 0
        return self.get_max_depth(root)


637. Average of Levels in Binary Tree - 获取二叉树每一层的平均值

n/a

Solution:

102. Binary Tree Level Order Traversal 的代码几乎完全一样,只是将 ret.append(level_vals) 简单替换成 ret.append(sum(level_vals)/len(level_vals)) 就可以,其它都不需要改。

n/a

虽然执行速度不算快,但是实际上也是稳定的 O ( n ) O(n) O(n) 算法。



构建(整棵)二叉树

108. Convert Sorted Array to Binary Search Tree

n/a

Solution:

个人认为针对这个问题而言,使用分治法来构建二叉树是再合适不过的了。
另外再通过递归实现分治法,整个实现其实相当简洁:

class Solution:
    def div_constructure_BST(self, nums):
        if not len(nums):
            return None
        if len(nums) == 1:
            return TreeNode(nums[0])

        half = len(nums) // 2
        middle = TreeNode(nums[half])
        middle.left = self.div_constructure_BST(nums[:half])
        middle.right = self.div_constructure_BST(nums[half + 1:])
        return middle

    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        return self.div_constructure_BST(nums)


109. Convert Sorted List to Binary Search Tree

n/a

Solution:

看到这个问题描述我还纠结了好一会儿如何合理地找到链表的中点。
但是后来转念一想,就算遍历一遍链表,也不过是 O ( n ) O(n) O(n) 的复杂度。
所以遍历一遍链表构建数组,再使用上面 108 实现过的方案,就可以解决这个问题了:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def div_constructure_BST(self, nums):
        [...code from 108 ...]

    def sortedListToBST(self, head: ListNode) -> TreeNode:
        nums = []
        curr = head
        while curr:
            nums.append(curr.val)
            curr = curr.next
        return self.div_constructure_BST(nums)



公共祖先问题

235. Lowest Common Ancestor of a Binary Search Tree

n/a
n/a

Solution:

这个问题同样可以使用递归实现。因为二叉搜索树的性质为寻找最低公共祖先简化了算法。

class Solution:
    def LCA(self, root, p, q):
        lower = min(p.val, q.val)
        upper = max(p.val, q.val)

        curr = root
        if curr.val > upper:
            return self.LCA(curr.left, p, q)
        elif curr.val < lower:
            return self.LCA(curr.right, p, q)
        else:
            return curr

    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        return self.LCA(root, p, q)


110. Balanced Binary Tree - 判断二叉树是否平衡

n/a
n/a

Solution:

一开始没搞清楚平衡二叉树的定义 - 实现成了判断是否是“完全平衡二叉树”了。
后来重新审视了一下定义。定义已经提供了很强烈的使用递归实现的说法。
所以这里使用递归的实现方法。虽然不会是最快的,但是也不会太慢。因为是 O ( n ) O(n) O(n) 的复杂度,每个节点也就遍历一次。

class Solution:
    def get_deepth(self, root):
        if root is None:
            return 0
        if root.left is None and root.right is None:
            return 1
        left_deepth = 0
        right_deepth = 0
        if root.left:
            left_deepth = self.get_deepth(root.left)
        if root.right:
            right_deepth = self.get_deepth(root.right)
        return max(left_deepth, right_deepth) + 1

    def is_balanced(self, root):
        if root is None:
            return True
        if not self.is_balanced(root.left):
            return False
        if not self.is_balanced(root.right):
            return False

        left_deepth = self.get_deepth(root.left)
        right_deepth = self.get_deepth(root.right)
        if abs(left_deepth - right_deepth) <= 1:
            return True
        else:
            return False

    def isBalanced(self, root: TreeNode) -> bool:
        return self.is_balanced(root)

这个实现唯一要问自己的就是是否有必要加缓存机制(如果对一个子节点不止遍历一次的话),但是最后分析看来是不需要。



112. Path Sum - 判断二叉树是否存在一条根到叶的路径使和为指定值

n/a

Solution:

注意满足条件的必须是“根”到“叶”这两个条件!

class Solution:
    def has_path_sum(self, root, _sum):
        if root is None:
            return False
        if root.left is None and root.right is None:
            if root.val == _sum:
                return True
            else:
                return False

        if self.has_path_sum(root.left, (_sum - root.val)):
            return True
        if self.has_path_sum(root.right, (_sum - root.val)):
            return True
        return False

    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        return self.has_path_sum(root, sum)



226. Invert Binary Tree – 翻转二叉树

n/a

Solution:

class Solution:
    def invert_tree(self, root):
        if root is None:
            return None
        left = self.invert_tree(root.right)
        right = self.invert_tree(root.left)
        root.left = left
        root.right = right
        return root

    def invertTree(self, root: TreeNode) -> TreeNode:
        return self.invert_tree(root)



404. Sum of Left Leaves

n/a

Solution:

def is_leaf(node):
    if node is None:
        return False
    if node.left is None and node.right is None:
        return True
    return False


class Solution:
    def sum_of_left_leaves(self, root):
        if root is None:
            return 0

        if root.left is None:
            val1 = 0
        elif is_leaf(root.left):
            val1 = root.left.val
        else:
            val1 = self.sum_of_left_leaves(root.left)
        
        if root.right is None:
            val2 = 0
        else:
            val2 = self.sum_of_left_leaves(root.right)
        return val1 + val2

    def sumOfLeftLeaves(self, root: TreeNode) -> int:
        if root is None:
            return 0
        return self.sum_of_left_leaves(root)


572. Subtree of Another Tree - 判断一棵二叉树是否是另一个二叉树的子树

Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and node values with a subtree of s. A subtree of s is a tree consists of a node in s and all of this node’s descendants. The tree s could also be considered as a subtree of itself.

n/a
n/a

Solution:

class Solution:
    def level_search(self, level, key):
        next_level = []
        ret = []
        if level:
            for node in level:
                if node is None:
                    continue
                if node.val == key:
                    ret.append(node)
                next_level.append(node.left)
                next_level.append(node.right)
            ret.extend(self.level_search(next_level, key))
        return ret

    def search_nodes(self, root, key):
        if root is None:
            raise RuntimeError("can't found")

        ret = []
        if root.val == key:
            ret.append(root)
        ret.extend(self.level_search([root.left, root.right], key))
        if not ret:
            raise Exception("can't found")
        return ret

    def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
        try:
            sub_roots = self.search_nodes(s, t.val)
        except Exception:
            return False
        else:
            return any([self.is_same_tree(_, t) for _ in sub_roots])

其中,.is_same_tree 用到了上文 100. Same Tree 实现过的函数。

n/a

在看题目的时候就在想,树中存不存在相同的值。因为这一题没有 Note,然后抱着错了就错了的心态提交了一次,确实是含有相同值的,于是修改代码,实现成将所有值等于 t.val 的子树都找出来判断是否有一个子树完全相同。



Reference



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值