和二叉树相伴的美好时光~@labuladong 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值