LeetCode刷题碎碎念(九):Tree
- 二叉树
- 144. 二叉树的前序遍历
- 二叉树的后序遍历
- 二叉树的中序遍历
- 637. 二叉树的层平均值(简单)
- 94. Binary Tree Inorder Traversal
- 98. Validate Binary Search Tree
- 543. Diameter of Binary Tree (Easy)
- 687. Longest Univalue Path(Easy)
- 104. Maximum Depth of Binary Tree (Easy)
- 199. Binary Tree Right Side View
- 987. Vertical Order Traversal of a Binary Tree (Medium)
- 105. Construct Binary Tree from Preorder and Inorder Traversal
- 173. Binary Search Tree Iterator
- 662. Maximum Width of Binary Tree
- 404. Sum of Left Leaves (Easy)
- 655. Print Binary Tree (Medium)
二叉树
144. 二叉树的前序遍历
- 递归
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
二叉树的后序遍历
- 递归
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
- 迭代
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
二叉树的中序遍历
- 递归
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
- 迭代
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)
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
- preorder中第一个是根节点
- inorder中找到根节点的位置:左边都是左节点,右边都是右节点
- 回到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)