文章目录
- 写在前面
- 二叉树思考学习记录
- Day3 二叉树迭代相关~
- Day3 [练习](https://alidocs.dingtalk.com/document/edit?dentryKey=RwEqz1ky2s4BKKn7#%20%E3%80%8C%E7%AC%AC%E4%B8%89%E5%A4%A9%E4%BD%9C%E4%B8%9A%E3%80%8D)
- 0.1 [前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/)
- 0.2 [中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)
- 0.3 [后序遍历](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/)
- 1.1 [层序遍历](https://leetcode-cn.com/problems/binary-tree-level-order-traversal)
- 1.2 [层序遍历2](https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii)
- 1.3 [锯齿遍历](https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal)
- 1.4 [每层最大值](https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row)
- 1.5 [最大层内元素和](https://leetcode-cn.com/problems/maximum-level-sum-of-a-binary-tree)
- 1.6 [层平均值](https://leetcode-cn.com/problems/average-of-levels-in-binary-tree)
- 1.7 [N 叉树层序遍历](https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal)
- 2.1 [岛屿数量](https://leetcode-cn.com/problems/number-of-islands)
- 2.2 [岛屿最大面积](https://leetcode-cn.com/problems/max-area-of-island)
- Day3 的思考与补充
写在前面
本篇全部集中在二叉树相关问题上,是参考东哥的思路进行的练习和思考。东哥有《labuladong 的算法小抄》以及宝藏微信公众号 labuladong,github 也有项目,自来水推荐购买和关注。
二叉树思考学习记录
Day3 二叉树迭代相关~
本篇聊的迭代有两方面的意思:
- 遍历二叉树不再使用递归函数的形式,而是使用迭代来遍历,不变的是“遍历”,迭代和递归只是为了进行“遍历”的手段而已。意义不大,只是卷起来了哈哈哈O(∩_∩)O~比较吃对栈的理解和操作。
- 对应 bfs 的思想,bfs 和 dfs 本质都在做穷举,很多问题都可以任选其一。但在某些特定问题上如无权最短路径问题,只有 bfs 能胜任。
Day3 练习
迭代的形式完成二叉树的遍历,注释出来具体在操作什么。
0.1 前序遍历
0.2 中序遍历
0.3 后序遍历
用 bfs 的方式完成以下题目:
1.1 层序遍历
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if not root:
return []
my_que = collections.deque()
my_que.append(root)
res = [[root.val]]
while my_que:
length = len(my_que)
temp = []
for i in range(length):
cur = my_que.popleft()
if not cur: continue
if cur.left:
temp.append(cur.left.val)
my_que.append(cur.left)
if cur.right:
temp.append(cur.right.val)
my_que.append(cur.right)
if temp:
res.append(temp)
return res
1.2 层序遍历2
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
if not root: return []
que = collections.deque()
res = collections.deque()
que.append(root)
res.append([root.val])
while que:
le = len(que)
temp = []
for i in range(le):
cur = que.popleft()
if cur.left:
que.append(cur.left)
temp.append(cur.left.val)
if cur.right:
que.append(cur.right)
temp.append(cur.right.val)
if temp: res.appendleft(temp)
return list(res)
1.3 锯齿遍历
完全用队列判断显得有点罗嗦了,其实也完全可以反转 list。这里反转的只有在取 val 的时候,而不是入队的时候。入队肯定都是从左到右的,不然下一层又要反过来。
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
if not root: return []
que = collections.deque()
res = collections.deque()
que.append(root)
res.append([root.val])
# the root is level 0 and has been pushed, so start from 1
even = False
while que:
le = len(que)
temp = collections.deque()
for i in range(le):
cur = que.popleft()
if cur.left:
que.append(cur.left)
if even:
temp.append(cur.left.val)
else:
temp.appendleft(cur.left.val)
if cur.right:
que.append(cur.right)
if even:
temp.append(cur.right.val)
else:
temp.appendleft(cur.right.val)
even = ~even
if temp: res.append(list(temp))
return list(res)
1.4 每层最大值
def largestValues(self, root: TreeNode) -> List[int]:
if not root: return []
que = collections.deque()
que.append(root)
res = [root.val]
while que:
le = len(que)
level_max = -float('Inf')
for i in range(le):
cur = que.popleft()
if cur.left:
que.append(cur.left)
level_max = max(level_max, cur.left.val)
if cur.right:
que.append(cur.right)
level_max = max(level_max, cur.right.val)
res.append(level_max)
return res[:-1]
1.5 最大层内元素和
def maxLevelSum(self, root: TreeNode) -> int:
cur_level = 1
cur_max = root.val
target_level = 1
que = collections.deque()
que.append(root)
while que:
cur_level += 1
le = len(que)
temp = []
for i in range(le):
cur = que.popleft()
if cur.left:
que.append(cur.left)
temp.append(cur.left.val)
if cur.right:
que.append(cur.right)
temp.append(cur.right.val)
# 未曾设想的道路,每层都是负的,最后 temp 为空也能 sum 得到 0 大于历史值,呱~
if not temp: break
sum_level = sum(temp)
if sum_level > cur_max:
target_level = cur_level
cur_max = sum_level
return target_level
1.6 层平均值
def averageOfLevels(self, root: TreeNode) -> List[float]:
que = collections.deque()
que.append(root)
res = [root.val]
while que:
le = len(que)
temp = []
for i in range(le):
cur = que.popleft()
if cur.left:
que.append(cur.left)
temp.append(cur.left.val)
if cur.right:
que.append(cur.right)
temp.append(cur.right.val)
if not temp: break
res.append(sum(temp) / len(temp))
return res
1.7 N 叉树层序遍历
def levelOrder(self, root: 'Node') -> List[List[int]]:
if not root:
return []
que = collections.deque()
que.append(root)
res = [[root.val]]
while que:
temp = []
len_que = len(que)
for i in range(len_que):
cur = que.popleft()
for child in cur.children:
if not child: continue
que.append(child)
temp.append(child.val)
if temp:
res.append(temp)
return res
同时用 DFS(递归遍历)方式和 BFS 方式完成下列题目:
其实岛屿问题跟传统图像处理里面的某些连通域操作有很大类似。
2.1 岛屿数量
本问题可以通过直接修改“岛屿”来实现不重复访问避免陷入死循环,都是通过 bfs 或者 dfs 的次数,来实现岛屿数量的计数。还可以用并查集做,但颈椎告诉我不要再写了O(∩_∩)O哈哈~
bfs:
def numIslands(self, grid: List[List[str]]) -> int:
height, width = len(grid), len(grid[0])
drcts = [[0, 1], [0, -1], [1, 0], [-1, 0]]
cnt = 0
def bfs(sr, sc):
grid[sr][sc] = '0'
que = collections.deque()
que.append([sr, sc])
while que:
le = len(que)
for i in range(le):
cr, cc = que.popleft()
for [dr, dc] in drcts:
nr, nc = cr + dr, cc + dc
if nr < 0 or nr > height - 1: continue
if nc < 0 or nc > width - 1: continue
if grid[nr][nc] == '0': continue
grid[nr][nc] = '0'
que.append([nr, nc])
return
for i in range(height):
for j in range(width):
if grid[i][j] != '1': continue
cnt += 1
bfs(i, j)
return cnt
dfs:
def numIslands(self, grid: List[List[str]]) -> int:
res = 0
height, width = len(grid), len(grid[0])
def dfs(row, col):
grid[row][col] = "0"
for (r, c) in [[row, col + 1], [row, col - 1], [row + 1, col], [row - 1, col]]:
if r < 0 or r > height - 1:
continue
if c < 0 or c > width - 1:
continue
if grid[r][c] == "1":
dfs(r, c)
for i in range(height):
for j in range(width):
if grid[i][j] == "1":
res += 1
dfs(i, j)
return res
2.2 岛屿最大面积
同数量问题很类似,只不过维护两个变量来求取最大面积,一个变量记录历史最大岛屿面积,一个变量记录当前岛屿面积的增长。岛屿面积的增长,在 bfs 是入队的时刻更新,在 dfs 是在 d 到它的时候更新。
bfs:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
max_area = 0
height, width = len(grid), len(grid[0])
drcts = [[0, 1], [0, -1], [1, 0], [-1, 0]]
def bfs(sr, sc):
grid[sr][sc] = 0
que = collections.deque()
que.append([sr, sc])
self.cur_area += 1
while que:
le = len(que)
for i in range(le):
cr, cc = que.popleft()
for [dr, dc] in drcts:
nr, nc = cr + dr, cc + dc
if nr < 0 or nr > height - 1: continue
if nc < 0 or nc > width - 1: continue
if grid[nr][nc] == 0: continue
grid[nr][nc] = 0
que.append([nr, nc])
self.cur_area += 1
return
for i in range(height):
for j in range(width):
if grid[i][j] != 1: continue
self.cur_area = 0
bfs(i, j)
max_area = max(max_area, self.cur_area)
return max_area
dfs:
class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
max_area = 0
height, width = len(grid), len(grid[0])
drcts = [[0, 1], [0, -1], [1, 0], [-1, 0]]
def dfs(sr, sc):
self.cur_area += 1
grid[sr][sc] = 0
for [dr, dc] in drcts:
nr, nc = sr + dr, sc + dc
if nr < 0 or nr > height - 1: continue
if nc < 0 or nc > width - 1: continue
if grid[nr][nc] == 0: continue
dfs(nr, nc)
return
for i in range(height):
for j in range(width):
if grid[i][j] != 1: continue
self.cur_area = 0
dfs(i, j)
max_area = max(max_area, self.cur_area)
return max_area
Day3 的思考与补充
滑动拼图游戏
使用 bfs 的思路来做游戏,仍然可以考虑优化的地方:
- 状态的表示,题目中我仍然使用的是二维数组,还可以使用整型或字符串来表示状态,应该可以优化一些时间常数;而且二维数组也不让放进集合里面,状态空间太大的时候,记录状态防止重复访问也不方便,in 方法太慢了;
- 游戏难度增加,状态空间太大的时候,还可以考虑使用双向 bfs 来求解,还妹尝试写过,颈椎现在也不让写。
import copy
def slidingPuzzle(self, board: List[List[int]]) -> int:
# 定义目标状态
target = [[1, 2, 3],[4, 5, 0]]
if board == target: return 0
height, width = len(board), len(board[0])
states = collections.deque()
states.append(board)
visited = [states]
drcts = [[0, 1], [0, -1], [1, 0], [-1, 0]]
# 寻找相邻状态
def find_neighbours(board):
neighbours = []
for i in range(height):
for j in range(width):
if board[i][j] == 0:
r0, c0 = i, j
for [dr, dc] in drcts:
nr, nc = r0 + dr, c0 + dc
if nr < 0 or nr > height - 1: continue
if nc < 0 or nc > width - 1: continue
board[r0][c0], board[nr][nc] = board[nr][nc], board[r0][c0]
neighbours.append(copy.deepcopy(board))
board[r0][c0], board[nr][nc] = board[nr][nc], board[r0][c0]
return neighbours
# 状态有效转移次数
cnt = 0
while states:
cnt += 1
le = len(states)
for i in range(le):
cur_state = states[i]
neighbour_states = find_neighbours(cur_state)
for ns in neighbour_states:
# 到达目标状态
if ns == target: return cnt
if ns in visited: continue
visited.append(ns)
states.append(ns)
for i in range(le):
states.popleft()
return -1