LeetCode刷题碎碎念(九):Tree

二叉树

二叉搜索树操作集锦

144. 二叉树的前序遍历

  1. 递归
class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        def preorder(root):
            if not root:
                return
            res.append(root.val)
            preorder(root.left)
            preorder(root.right)
        preorder(root)
        return res

时间复杂度:O(n)O(n),其中 nn 是二叉树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n)O(n),为递归过程中栈的开销,平均情况下为 O(\log n)O(logn),最坏情况下树呈现链状,为 O(n)O(n)。
2. 迭代

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        if not root:
            return res
        stack = []
        node = root
        while stack or node:
            while node:
                res.append(node.val)
                stack.append(node)
                node = node.left    # 永远先把左树遍历完
            node = stack.pop()  # 左树遍历完 往回走
            node = node.right
        return res

二叉树的后序遍历

  1. 递归
class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        def postorder(root):
            if not root:
                return
            postorder(root.left)
            postorder(root.right)
            res.append(root.val)    # append root放在最后
        postorder(root)
        return res
  1. 迭代
class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        if not root:
            return res
        
        stack = []
        prev = None
        while root or stack:
            while root:
                stack.append(root)
                root = root.left

            root = stack.pop()

            if not root.right or root.right == prev:
                res.append(root.val)    # 这就是左子树的最深
                prev = root
                root = None # 为了使得下一个root为stack pop出来的元素
                            # 完成从左子树叶子节点返回上一级
            else:
                stack.append(root)
                root = root.right
        return res

二叉树的中序遍历

  1. 递归
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        def inorder(root):
            if not root:
                return
            inorder(root.left)
            res.append(root.val)
            inorder(root.right)
        
        inorder(root)
        return res
  1. 迭代
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        if not root:
            return res
        stack = []
        while root or stack:
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            res.append(root.val)
            root = root.right
        return res

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

https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/
在这里插入图片描述

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

class Solution:
    # BFS
    def averageOfLevels(self, root: TreeNode) -> List[float]:
        if not root:
            return []
        
        res = []
        queue = [root]      # 保存当前层的TreeNode
        while queue:
            curr = []       # 保存当前层的所有node的值
            nxt = []        # 保存下一层TreeNode
            for node in queue:
                curr.append(node.val)
                # 向下一层
                if node.left:
                    nxt.append(node.left)
                if node.right:
                    nxt.append(node.right)

            res.append(sum(curr)/len(curr))
            queue = nxt # 进入下一层
        
        return res

94. Binary Tree Inorder Traversal

树的遍历
BFS:一层一层遍历
DFS:inorder, preorder, postorder 递归或stack

class Solution:
    # DFS递归
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        return self.helper(res, root)
    
    def helper(self, res, root):
        if root ==  None:	# left or right树为空,回到dfs上一个节点
            return
        self.helper(res, root.left)	# dfs左子树向下走
        # 如果再没有左子树
        res.append(root.val)	# 存当前节点到遍历结果中
        # 还有右子树吗?
        self.helper(res, root.right) # 如果右子树也没有(当前节点左右子树都没没有了):由于return nothing,
        							 # 就会一层一层向上返回节点
        							 # 如果有右子树,对右子树继续按上述顺序遍历
		# 每次检查完左 + 右子树都会返回父节点
        return res

利用stack,更形象。

class Solution:
    # stack
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return
        res = []
        stack = []  # 储存当前处理的树的一支: 因为要先返回左子树的叶子节点,所以利用stack完成倒序输出
        while stack or root:
            if root:
                stack.append(root)
                root = root.left    # 先遍历左子树
            # root == None 走到了叶子节点;stack非空:有子树
            else:   # 看当前的父节点是否有右子树,向上寻找,一直找到stack中左子树的根节点
                node = stack.pop()	# 左子树倒序
                res.append(node.val)    # 把左子树输出
                root = node.right   # 找右子树
                
        return res

98. Validate Binary Search Tree

# 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 isValidBST(self, root: TreeNode) -> bool:
        res = []
        self.inOrder(root, res)
        
        # 因为二叉树左 < 根 < 右;中序遍历返回左子树+根节点+右子树
        # 所以大小应该从小到大
        for i in range(1, len(res)):
            if res[i-1] >= res[i] :
                return False
        return True
        
    # 对书进行中序遍历
    def inOrder(self, root, res):
        if not root:
            return
        self.inOrder(root.left, res)
        res.append(root.val)
        self.inOrder(root.right, res)

543. Diameter of Binary Tree (Easy)

在这里插入图片描述

# 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 diameterOfBinaryTree(self, root: TreeNode) -> int:
        self.res = 0
        self.LP(root)
        return self.res
        
    def LP(self, root): # 把每个root当作调头节点(转折点)
        if not root:
            return -1
        left = self.LP(root.left) + 1   # 求当前节点左右子树的最长路径
        right = self.LP(root.right) + 1 # +1:加一步到当前节点
        self.res = max(self.res, left + right)
        return max(left, right) # 返回左右子树中一条较长的单边节点给root         

687. Longest Univalue Path(Easy)

路径上的值必须都是一样的

class Solution:
    def longestUnivaluePath(self, root: TreeNode) -> int:
        self.max_val = 0
        
        def dfs(node, prev_val):
            if not node: return 0
            left = dfs(node.left, node.val)
            right = dfs(node.right, node.val)
            self.max_val = max(self.max_val, left + right)
            if node.val == prev_val:
                return max(left, right) + 1
            return 0
        
        if not root: return 0
        dfs(root, root.val)
        return self.max_val

104. Maximum Depth of Binary Tree (Easy)

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

Recursion动图

199. Binary Tree Right Side View

同样的层次遍历,这次把放入queue的顺序变为先right后left,然后取每层第一个元素,就是最右边。

class Solution:
    def rightSideView(self, root: TreeNode) -> List[int]:
        res = []
        if not root: return res
        
        queue = [root]
        while queue:
            nxt = []
            cur = []
            for node in queue:
                cur.append(node.val)
                if node.right: 
                    nxt.append(node.right)
                if node.left: 
                    nxt.append(node.left)
            res.append(cur[0])
            queue = nxt
        return res

987. Vertical Order Traversal of a Binary Tree (Medium)

第一遍遍历:[y, x] 树中的坐标存入vals数组中 sort by y
第二遍遍历:输出结果

时间复杂度:O (n log n)
空间复杂度:O (n)

class Solution:
    def verticalTraversal(self, root: TreeNode) -> List[List[int]]:
        node_list = []
        
        # bfs获取所有节点的(列,行,值)信息,生成列表node_list
        def BFS(root):
            queue = deque([(root, 0, 0)])
            while queue:
                node, row, column = queue.popleft()
                if node is not None:
                    node_list.append((column, row, node.val))   # 将每一个节点储存为(列,行,值)
                    queue.append((node.left, row + 1, column - 1))  # 子节点row+1 (不同于题目描述-1)
                    queue.append((node.right, row + 1, column + 1))
                    
        # step 1). construct the global node list, with the coordinates
        BFS(root)

        # step 2). sort the global node list, according to the coordinates
        # 按列号进行排序,从小到大
        node_list.sort()

        # step 3). retrieve the sorted results partitioned by the column index
        ret = OrderedDict()
        for column, row, value in node_list:
            if column in ret:
                ret[column].append(value)   # 两个节点在同一列,加一个
            else:
                ret[column] = [value]       # 新增列
        return ret.values()

Input:[3,9,20,null,null,15,7]
node_list: [(0, 0, 3), (-1, 1, 9), (1, 1, 20), (0, 2, 15), (2, 2, 7)]
第一个node的x,y均为0,为起始点。后面的x,y代表和起始点的相对位置,用来识别其在哪一列。

105. Construct Binary Tree from Preorder and Inorder Traversal

  1. preorder中第一个是根节点
  2. inorder中找到根节点的位置:左边都是左节点,右边都是右节点
  3. 回到preorder,根据刚刚的左右子树,分别找出左右子树的根节点(第一个节点)…
# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        val_index = dict()
        for i in range(len(inorder)):   # val_index
            val_index[inorder[i]] = i   # {node value: index in inorder}
        
        def helper(preStart, inStart, inEnd):
                # preStart: 1-st node in preorder --> root node
                # [inStart, inEnd]: range for tree taking preStart as the root
                
                if preStart >= len(preorder) or inStart > inEnd:
                    return None
            
                root = TreeNode(preorder[preStart])
                rootIndex = val_index[root.val]
                
            # 左子树的根节点在preorder中的位置 preStart + 1: 根节点后肯定是左子树的根节点
            # [inStart, rootIndex - 1]: 左子树在inorder的index范围
                root.left = helper(preStart + 1, inStart, rootIndex - 1)
            # 右子树跟节点在preorder中的位置 preStart+(rootIndex-inStart +1)
            # 跟节点加上左子树的节点个数--> 跳到了右子树的在preorder的第一个节点(根节点)
                root.right = helper(preStart + rootIndex - inStart + 1, rootIndex + 1, inEnd)
                    
                return root
    
        return helper(preorder, inorder, 0, 0, len(inorder), val_index)

** 获取dict中值对应的键:

d = {"one": 1, "two": 2}
print( list (d.keys()) [list (d.values()).index(1)] )

** nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量。

def work():
    x = 0
    def new_work():
        nonlocal x
        x=x+3
        return x
    return new_work
# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def helper(in_left = 0, in_right = len(inorder)):
            nonlocal pre_idx
            # inorder的左端和右端index
            if in_left == in_right:
                return None
            
            root_val = preorder[pre_idx]    # 从preorder中取root:第一个node
            root = TreeNode(root_val)       # 根据root的value构建root的node
            index = idx_map[root_val]       # 取root在inorder中的index
            
            pre_idx += 1    # preorder位置向后移,准备找下一个root
            # root将inorder分成左右子树,构建出左右子树
            root.left = helper(in_left, index)
            root.right = helper(index + 1, in_right)
            return root
        
        pre_idx = 0
        # 储存inorder的 node值 - 索引 对应,用来反复依靠root来找左右子树
        idx_map = {val:idx for idx, val in enumerate(inorder)}
        return helper()
        

173. Binary Search Tree Iterator

# 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: TreeNode):
        self.stack = list()
        self.cur = root

    def next(self) -> int:
        """
        @return the next smallest number
        """
        while self.cur != None:
            self.stack.append(self.cur)
            self.cur = self.cur.left    # 把所有的左子树放进去,直到没有(最后一个放进去的是最小值)
        # 最小值
        self.cur = self.stack.pop()     
        val = self.cur.val
        # 左 < 根 < 右:当前节点的右节点肯定比马上要返回的上一级节点小(因为仍然处于上一级节点的左子树)
        self.cur = self.cur.right     # 检查右子树  
        return val

    def hasNext(self) -> bool:
        """
        @return whether we have a next smallest number
        """
        if self.stack or self.cur:
            return True
        else:
            return False


# Your BSTIterator object will be instantiated and called as such:
# obj = BSTIterator(root)
# param_1 = obj.next()
# param_2 = obj.hasNext()

662. Maximum Width of Binary Tree

在这里插入图片描述
如果给每一个点(包括没有节点的地方)一个id,会超出内存。
我们重新定义一种id:new_id = id - 这一深度中最小的id(到这个节点的宽度)。
所以一层中,最左节点id一定是0,最右就是该层的宽度。

O( n )

404. Sum of Left Leaves (Easy)

class Solution:
    def sumOfLeftLeaves(self, root: TreeNode) -> int:
        if not root: return 0
        # 左节点存在and左节点没有子节点 ——> 左叶子节点
        if root.left and not root.left.left and not root.left.right:
            # 当前左叶子节点 + 左叶子节点的父节点的右子树的左叶子节点
            return root.left.val + self.sumOfLeftLeaves(root.right)
        
        # 左子树的左叶子节点 + 右子树的左叶子节点
        return self.sumOfLeftLeaves(root.left) + self.sumOfLeftLeaves(root.right)

655. Print Binary Tree (Medium)

二叉树的宽度 = 2 ^ 高度 - 1

class Solution:
    def printTree(self, root: TreeNode) -> List[List[str]]:
        # 递归获得树的高度
        def height(root):
            if not root: return 0
            return max(height(root.left), height(root.right)) + 1
        
        def fill(root, h, l, r):
            if not root: return
            mid = (l + r) // 2  # 填充根节点的位置
            m[h][mid] = str(root.val)   # 填充根节点
            if root.left:   # 递归填充左子树
                fill(root.left, h + 1, l, mid - 1)
            if root.right:  # 递归填充右子树
                fill(root.right, h + 1, mid + 1, r)
        
        h = height(root)
        w = 2 ** h - 1
        m = [[""] * w for _ in range(h)]
        fill(root, 0, 0, w - 1)
        return m

时间复杂度 O(2^h), 空间复杂度 O(2^h)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值