本文是根据穷码农的LeetCode刷题建议而进行专项练习时记录的心得。
最近发现自己练习时用的方法不太正确,老是想着自己先完全原创一个方法,实在写不动了才去看解析。这样做虽然有一定效果,但却非常耗时。其中,有一道中等题我前前后后花了6个小时才做完,结果发现自己之前做着做着就开始抠起了细节,完全没去用算法思想来解题,实在是得不偿失。以后,我不会再在一道题上死磕这么久了,在一个番茄钟的时间结束后如果没有想法或者思路不清晰时,我就直接查看解析。然后,根据解析重新背着写完这道题,并总结它的规律,及时应用于下一道题的训练。
今天的笔记包含基于树的宽度优先搜索(Tree Breadth-First Search)类型下的5个题目,它们在leetcode上的编号和题名分别是:
下面将根据以上顺序分别记录代码和对应心得,使用的编译器为Pycharm (Python3)。
Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).
For example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its level order traversal as:
[
[3],
[9,20],
[15,7]
]
方法一:利用队列循环遍历。通过Python中的普通列表模仿队列,将每层元素推进去,并统一取出来以一个列表的形式推入需要返回的列表中。最后再看统一取出来的节点是否有左右子节点,若有则将这些子节点推入"队列"中去周而复始,直到没有子节点为止。
class Solution:
def levelOrder(self, root: TreeNode) ->list:
ans = []
if root is None:
return ans
stack = []
temp = [] # used to fill the elements in each level
stack.append(root)
while len(stack) != 0:
# get the element from the level of stack
while len(stack) != 0:
temp.append(stack.pop(0))
# record the values of each element from the same level
values = []
while len(temp) > 0:
# put the element into answer list
values.append(temp[0].val)
# check if each element has children
if temp[0].left is not None:
stack.append(temp[0].left)
if temp[0].right is not None:
stack.append(temp[0].right)
temp.pop(0)
ans.append(values)
return ans
方法二:方法一的优化。方法一虽然借助了队列思想,但在具体处理的时候反复计算列表长度,并且不断使用pop(0)来剔除列表第一个元素并左移其他元素,时间消耗过大。
因此,方法二中开辟了新的临时变量,代表下一层元素的信息。利用for循环遍历完上一层信息后,直接用下一层的值替换上一层,不再需要将本层的节点抛出后再装入下一层的节点。
class Solution:
def levelOrder(self, root: TreeNode) ->list:
# special considerations
if root is None:
return []
# parameters
ans = []
cur_level = [root] # define the elements in each level
while cur_level:
next_level = [] # define next level
values = [] # define the values of each element in the same level
# essential: use 'for' loop to get each node info
for node in cur_level:
values.append(node.val)
if node.left is not None:
next_level.append(node.left)
if node.right is not None:
next_level.append(node.right)
ans.append(values)
cur_level = next_level
return ans
Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root).
For example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its bottom-up level order traversal as:
[
[15,7],
[9,20],
[3]
]
此题采用BFS时,首先应该考虑的就是遍历树,通过构建一个队列去承载树每层的节点,再根据队列提取我们需要的节点信息。因此,此题解法和上一题完全相同,仅需注意最后添加节点信息的顺序问题。
class Solution:
def levelOrderBottom(self, root: TreeNode) -> list:
# solution 1:队列。采用BFS时,首先应该考虑的就是遍历树,通过构建一个队列去承载树每层的节点,再根据队列提取我们需要的
# 节点信息。
# special considerations:
if root is None:
return []
# parameters
ans = []
cur_level = [root]
while cur_level:
next_level = []
temp = [] # get the values of each node on the same level
for node in cur_level:
temp.append(node.val)
if node.left is not None:
next_level.append(node.left)
if node.right is not None:
next_level.append(node.right)
# insert the node info to the first position
ans.insert(0, temp)
cur_level = next_level
return ans
Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between).
For example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its zigzag level order traversal as:
[
[3],
[20,9],
[15,7]
]
ZigZag在这里也和上一题的解法一致,同样只需注意最后添加信息的顺序问题。
class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> list:
# solution: 类似于普通的层序遍历。区别就是遍历时需注意每层提取的节点顺序不同。
# special considerations
if root is None:
return []
# parameters
ans = []
cur_level = [root] # define the elements in each level
left_right = True
while cur_level:
next_level = [] # define next level
values = [] # define the values of each element in the same level
# essential: use 'for' loop to get each node info
for node in cur_level:
values.append(node.val)
if node.left is not None:
next_level.append(node.left)
if node.right is not None:
next_level.append(node.right)
if left_right:
ans.append(values)
left_right = False
else:
values = values[::-1]
ans.append(values)
left_right = True
cur_level = next_level
return ans
Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.
Example:
Input: [1,2,3,null,5,null,4]
Output: [1, 3, 4]
Explanation:
1 <---
/ \
2 3 <---
\ \
5 4 <---
首先创建一个队列用于承载树每层的节点。遍历树,每次只取队列最尾部的节点的值,然后找到队列里所有节点的子节点,按照从左到右的顺序依次装入新的队列里,再循环。直到队列没有节点可装为止。
我一开始做这道题就想着通过观察法一层一层地寻找最右边的节点,但这样无非就是给自己挖坑,细节顾不过来。正确的方法是,利用队列 (数据结构)的构造,将树每层的节点从左到右依次排放在一个队列里,这样就能迅速定位最右边的节点。做题时不能想着观察法,还是得用数据结构这个工具去辅助,才能达到高效率。
class Solution:
def rightSideView(self, root: TreeNode) -> list:
# solution: 队列+迭代。
# special consideration
if root is None:
return []
# parameters
ans = []
queue = [root]
while len(queue) != 0:
next_level_queue = []
# only get the last node's value (critical)
ans.append(queue[-1].val)
# get the next generation
for node in queue:
# pay attention to the order of putting nodes
if node.left is not None:
next_level_queue.append(node.left)
if node.right is not None:
next_level_queue.append(node.right)
queue = next_level_queue
return ans
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
Note: A leaf is a node with no children.
Example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its minimum depth = 2.
此题使用了DFS(深度优先),作为下一期训练的预热吧。这里我运用了递归,需要注意的是寻找循环的终止条件(当前节点无子节点)和层与层之间的规律(只要有子节点,就把它们当成下一次递归的参数,继续循环下去)。需要注意的是,得优先考虑节点的左右孩子都不为空的情况,这样可以避免先考虑左/右孩子而导致情况不全的问题。
class Solution:
def minDepth(self, root: TreeNode) -> int:
# solution: 递归。
# special consideration
if root is None:
return 0
# base
if root.left is None and root.right is None:
return 1
# patterns between layers
if root.left is not None and root.right is not None:
return min(1 + self.minDepth(root.left), 1 + self.minDepth(root.right))
if root.left is None:
return 1 + self.minDepth(root.right)
if root.right is None:
return 1 + self.minDepth(root.left)
在我练完上面的题后,发现树上的BFS解题套路特别明显:借助队列。凡是涉及到树的层次遍历,都逃不了使用队列这个数据结构去装载它每层的节点信息。以后遇到类似的题型时,可以优先考虑运用队列思想。
如果笔记存在一些问题,发现后我会尽快纠正。
*注:本文的所有题目均来源于leetcode