【LeetCode刷题笔记】DFS&BFS(二)

本文详细介绍了LeetCode中涉及DFS(深度优先遍历)和BFS(广度优先遍历)的经典问题,包括994.腐烂的橘子、542.01矩阵、1162.地图分析等,通过实例解析多源BFS和排列组合问题的解决策略。重点讨论了如何判断出发点和目标,以及如何进行剪枝优化,提高解题效率。
摘要由CSDN通过智能技术生成

994. 腐烂的橘子(树/图的BFS问题)

解题思路:
  • 多源BFS ,首选找到 所有的腐烂的橘子 ,放入队列中,然后进行 BFS 广搜,广搜的 层数 - 1 就是所需要花费的分钟数。
  • 在最开始先扫描一遍二维数组,将所有的 腐烂的橘子 加入 队列 ,同时统计新鲜橘子的数量 freshCount
  • 如果扫描完毕发现没有找到 腐烂的橘子 ,则此时可以直接确定答案:若 freshCount>0 则答案为 -1 (不可能全部腐烂),否则答案为 0 (即  分钟时就没有新鲜的橘子了)。
  • 如果有找到 腐烂的橘子 ,则进行 按层 BFS ,每次取出队列的大小,访问这一层的每个腐烂的橘子,
  • 在每一层中,查看腐烂橘子的四个方向的邻居,如果 邻居未越界且 是新鲜的橘子,就将其腐烂,并入队,同时将 freshCount - 1 在访问完一层后,层索引 level++
  • 最终看 freshCount 是否还有剩余,如果还有 剩余, 说明还存在新鲜橘子,答案为 -1 (没能全部腐烂) ,否则答案为  level - 1 ( 最后一层会腐烂0个橘子 ) 。

在第 5 次 BFS 中会腐烂 0 个橘子,然后退出BFS的循环,上图没有画出(注:第 4 次 BFS 中会将上图中最后一个橙黄色的 2 入队,进入第 5 次 BFS 时,会取出这个 2 然后发现它周围没有橘子可腐烂了,此时退出整个BFS)。所以总共会执行 5 层BFS搜索,但只需要 4 分钟即可全部腐烂完毕。因此答案是 level - 1

542. 01 矩阵(树/图的BFS问题)

解题思路:
  • 1. 多源BFS ,类似994.腐烂的橘子,对于矩阵中的每一个元素,如果是 0 ,那么离它最近的 0 就是它自己,因此 0 的位置距离是 0
  • 因此可以 从所有的 0 开始出发进行 BFS,寻找未访问过的 1 ,并将该位置的 距离 设置为当前 BFS的层数
  • 首先遍历一遍矩阵将遇到的  全部入队,并标记已访问,然后只要队列不为空,就取出当前队列大小,访问这一层的节点,
  • 每次取出当前队首的位置,访问它的上下左右四个邻居,如果邻居未访问过且没有越界,就将邻居坐标入队并标记已访问,同时设置邻居的距离 到邻居的距离值就是当前搜索的层数
  • 注意:标记是否已访问可以使用 boolean[][] visited 数组,也可以在原矩阵上修改,初始时将所有的 1 改成 -1
为什么需要 从 0 出发广搜 1 ,而不是从 1 出发去广搜 0?
  • 首先矩阵中两个数字之间距离是相互等价的:从 1 到 0 的距离就是从 0 到 1 的距离。
  • 假设10000x10000的矩阵中只有一个0,那么如果从1出发去广搜0的话,需要对剩余所有的1都进行一遍BFS来更新到这个0的距离,也就是需要进行 99,999,999 次BFS。
  • 但是如果我们从这个0出发去广搜1的话,只需要进行 1 次BFS即可知道这个0到每个1的距离(也就是每个1到这个0的距离)。
  • 当矩阵中有很多个0的时候,我们可以把这些 0 看成一个整体的「超级零」,它与矩阵中所有的 0 相连,这样的话,就与矩阵中只有一个 0 的情况一样了,那么怎样才能做到看成一个整体的「超级零」呢?开始就提前把所有的 0 全部加入队列,然后再开始 BFS,这就相当于从这个整体的「超级零」出发向外进行一圈一圈的BFS搜索。

 

这题代码结构几乎与994. 腐烂的橘子一样,注意两点:

  1. 更新答案的时机是在 BFS 搜索将邻居入队的时候,更新的是邻居格子的答案(邻居是值为1的格子)。
  2. 需要将统计层数的代码level++放在每一层访问的开头,因为访问该层的邻居节点时就需要使用到层数了,所以需要提前统计。

下面是使用提前将矩阵中的 1 置为 -1 的方式来代替 visited 数组的版本: 

因为矩阵中除了 0 就是 1,将所有的 1 置为 -1 后,只需判断遇到的格子只要是 -1 就表示是未访问过的,将该格子的值更新为当前搜索的层数 level 即可。

解题思路:
  • 2. 动态规划 ,我们用 dp[i][j] 来表示该位置距离最近的 0 的距离。对于任一点 (i, j) ,距离 0 的距离为:
  • if matrix[i][j] == 1,f(i, j) = 1 + min(dp(i - 1, j), dp(i, j - 1), dp(i + 1, j), dp(i, j + 1))  
  • if matrix[i][j] == 0,dp(i, j) = 0                                                    
我们发现 dp[i][j] 是由其 上下左右 四个状态来决定,无法从一个方向开始递推!
例如被四个 0 包围的  1 ,从中心位置的  1 移动到这四个 0 ,就需要使用四种不同的方法:
  • 1) 只有 水平向左 移动 和 竖直向上 移动;
  • 2) 只有 水平向左 移动 和 竖直向下 移动;
  • 3) 只有 水平向右 移动 和 竖直向上 移动;
  • 3) 只有 水平向右 移动 和 竖直向下 移动。

  • 因此我们分别从四个角开始递推,就得到了位于 「左上方、右上方、左下方、右下方」 距离 (i,j) 的最近的  0 的距离。
  • 优化:从四个角开始的 4 次递推,还可以优化成从任一组对角开始的 2 次递推,比如只写从 左上角 右下角 开始递推就行了。

下面是从左上角开始扫描的流程示意:

为了方便处理for循环中的边界条件判断,我们可以在dp数组的上下左右各添加一行(防御编程思想),并初始化为最大值,
然后就可以省去各种条件判断,处理中间的部分的dp时,只需要考虑左上和右下的最小值+1即可:dp[i][j] = min(dp[i][j], min(左, 上) + 1)   或 dp[i][j] = min(dp[i][j], min(右, 下) + 1)

下面是从左上角开始扫描的流程示意:

此题dp解法较难想到。

1162. 地图分析(树/图的BFS问题)

解题思路:
  • 1. 多源BFS 从陆地出发 向上下左右四周搜索海洋 ,搜索的 最大 层数 就是离陆地最远的海洋距离。
  • 首先遍历一遍矩阵,将所有遇到的 陆地 坐标 入队 。结束后判断如果队列大小为 0 ,说明全是 海洋 ,如果队列大小为 NxN ,说明全是 陆地 ,这两种情况都返回 -1
  • 然后从陆地开始进行 BFS ,每次取出当前队列大小,计数层数 level++ , 访问这一层的每个节点,每次取出队首元素坐标,然后看四周的邻居坐标,如果是海洋就入队并标记(或利用原数组置为 陆地 )。
  • 最后返回BFS的 层数 - 1 ,因为是从陆地出发的, 第一层是陆地 ,所以层数要 - 1。(这跟腐烂的橘子求解答案一样)

这题跟542题非常相似,都是反向进行多源BFS,542是从所有的 0 出发找 1,这题是从所有的 1 出发找 0,要寻找的那一方就是题目要更新答案的那一方。

解题思路:
  • 2. 动态规划 ,代码跟542. 01 矩阵 dp解法类似,做两次dp,从左上到右下和从右下到左上。
  • 先在dp数组的上下左右各添加一行,以处理边界问题,然后将
  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值