相信许多正在为算法面试做着准备刷着题的程序员都会有类似的焦虑:我刷够题了吗?还要再来点吗?到底刷到什么程度才够呢?
刷题究竟应该怎么刷?
- 刷题绝不是死记硬背。
- LeetCode 上总共近 1700 道题,这看起来很恐怖,实际上很多问题本质上是很类似的,不过是做了一些小的变化。99%不敢说,至少90%的算法题,对应的解题模式不外乎那十多种常见的套路。
- 我们应该通过若干道题来总结掌握一个通用的解题模式,然后举一反三,去解决一批问题。
这边对常见的解题模式(套路),分析了 相关问题如何进行识别,给出了 具体的模板,同时每个模式都列出了 若干经典题和高频题,在实战中加深理解。
P.S. 本文为个人刷题心得总结,如有问题,欢迎交流探讨
Breadth First Search in Binary Tree (树的BFS)
问题特点
- 要求 按照层的顺序 对树进行操作
方法思路
树的宽度优先搜索 (Breadth First Search, BFS) 是借助 队列(queue) 的数据结构来实现的【队列(queue) 是 BFS 的 标配】。
在 Python 中,可以借助 collections.deque
实现队列的数据结构。
先来看看 BFS 最基本的 实现方式:
- 首先创建一个队列,将 根结点入队
- 当队列不为空时:
- 队列头部的 结点 出队
- 访问 该结点
- 该结点的 左右儿子入队
from collections import deque
def level_order_traverse(self, root: TreeNode) -> List[List[int]]:
# 检查异常输入
if root == None:
return []
res = []
# 1.创建队列,根节点入队
queue = deque()
queue.append(root)
while queue: # 2.当队列不为空时
# 3.队列头部的节点出队
node = queue.popleft()
# 4.访问该节点,此处假设访问操作就是将节点值添加到 res 中
res.append(node.val)
# 5.该节点的左右儿子入队
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
return res
这种方式的缺点是,遍历是一次到底的,没有对不同层进行区分,不知道当前的层数。
如果需要 在宽搜的同时 对层进行区分 【比如 (中等) 二叉树的层序遍历 - Binary Tree Level Order Traversal】,常见的有下面三种方式:
① 1 Queue + Dummy Node
② 2 List
使用两个列表,存储这一层和下一层的节点
def level_order_traverse(self, root: TreeNode) -> List[List[int]]:
if root == None:
return []
res = []
cur_level_nodes = [root] # 存储当前层的节点
while cur_level_nodes:
cur_level = [] # 存储当前层的值序列
next_level_nodes = [] # 存储下一层的节点
for node in cur_level_nodes:
cur_level.append(node.val)
if node.left:
next_level_nodes.append(node.left)
if node.right:
next_level_nodes.append(node.right)
res.append(cur_level)
cur_level_nodes = next_level_nodes
return res
③ 1 Queue【推荐】
在原来的基本流程上进行修改,添加一个 for 循环
from collections import deque
def level_order_traverse(self, root):
if root == None:
return []
res = []
queue = deque()
queue.append(root)
# 如果需要,可以记录当前网络的层数
# level_index = 1
while queue:
level = [] # 存储当前层节点的层序遍历序列
level_length = len(queue) # 当前层节点的数目
for _ in range(level_length): # for 当前层的节点,得到下一层的节点
node = queue.popleft()
level.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
res.append(level)
# level_index += 1
return res
和 DFS 不同,树的 BFS 问题一般没什么变化,都是一样的套路
典型问题
① (简单) 二叉树的层次遍历 II - Binary Tree Level Order Traversal II
# 要求自底向上
# 在一般的层次遍历的基础上,将 res 反转(倒序) 一下即可
from collections import deque
class Solution:
def levelOrderBottom(self, root):
if root == None:
return []
res = []
queue = deque()
queue.append(root) # 根节点入队
while queue:
level_vals = [] # 存储当前层节点的遍历序列
level_length = len(queue) # number of nodes in current level
for _ in range(level_length):
node = queue.popleft()
level_vals.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
res.append(level_vals)
return res[::-1]
② (中等) 二叉树的锯齿形层次遍历 - Binary Tree Zigzag Level Order Traversal
# 思路一、对层数的奇偶性进行判断,偶数层 reverse 一下
from collections import deque
class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
if root == None:
return []
res = []
queue = deque()
queue.append(root) # 根节点入队
level_index = 1
while queue:
level_vals = [] # 存储当前层节点的遍历序列
level_length = len(queue) # number of nodes in the current level
for _ in range(level_length):
node = queue.popleft()
level_vals.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if level_index % 2 == 1:
res.append(level_vals)
else:
res.append(level_vals[::-1])
level_index += 1
return res
# 思路二、定义一个翻转标志(flag) is_reverse
from collections import deque
class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
if root == None:
return []
res = []
queue = deque()
queue.append(root) # 根节点入队
is_reverse = False
while queue:
level_vals = []
level_length = len(queue) # number of nodes in the current level
for _ in range(level_length):
node = queue.popleft()
level_vals.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if is_reverse:
res.append(level_vals[::-1])
else:
res.append(level_vals)
is_reverse = not is_reverse
return res
③ (简单) 将二叉树按照层级转化为链表 · Convert Binary Tree to Linked Lists by Depth
④ (简单) 二叉树的最小深度 - Minimum Depth of Binary Tree
利用宽度优先搜索,按照层的顺序去搜索,第一个搜索到的叶子节点就是最小深度的节点。
from collections import deque
class Solution:
def minDepth(self, root: TreeNode) -> int:
if root == None:
return 0
level_index = 1
queue = deque()
queue.append(root)
while queue:
level_length = len(queue)
for i in range(level_length):
node = queue.popleft()
if node.left == None and node.right == None:
return level_index
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
level_index += 1
复杂度分析
对于节点个数为 n 的二叉树:
- 时间复杂度:平均时间复杂度为 O(logn)