2. Leetcode算法笔记 - 太平洋大西洋水流问题,深度优先搜索
依然是算法笔记,这次记录下Leetcode上个星期三(4月27日)太平洋大西洋水流问题,基于官方题解整理的算法笔记,使用的是深度优先遍历,昨天终于出了一道困难题(第一反应:完蛋,连续打卡要断了),但是那题其实注意下细节,还是能做出来的,例如说<A></A><B></B>
是不合法的,虽然在html中似乎是有效的,那是因为浏览器已经帮我们加上了<html></html>
所以有效了。
(English version translate by GPT-3.5)
#原题
原题链接: 417. 太平洋大西洋水流问题 - 力扣
1 | 有一个 m × n 的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。 |
深入理解
这题当时刚开始做的时候没啥思路(我发现我涉及深度遍历就犯晕,看到那转来转去就晕了),但是万事就缺思路,有了思路,这题就会一半了。这题作为中等题,要求求出同时往太平洋和大西洋流出的水流,其实可以这么算(官方题解思路),先算出太平洋流向大西洋的水流,然后算出大西洋流向太平洋的水流,最后对2个数组求交集就是最终结果。分解思路:
- 首先就是得定义2个数组,用来存储太平洋流向大西洋,以及大西洋流向太平洋的水流方块
- 然后构建4个循环,分别递归计算太平洋横向流向大西洋的水流,然后计算太平洋纵向流向大西洋的水流,以及大西洋流向太平洋横纵向的水流
- 最后合并2个数组中存在共同流向的部分
- 在递归中,不断判断并递归4个方向(上下左右)的“山”是否能够流向对面,同时判断好这几个方向的岩石是否更高,如果更高,递归这个更高的岩石能够流向哪里。
编码开始
首先就是定义几个变量,分别是记录行,列的,以及它高度的变量
1
2
3
4
5
6// 定义数组行,即【】,【】,【】
private int row;
// 定义数组列,即【【....】】,【[....]】
private int col;
// 暂存高度值,备用
private int[][] heights;然后,定义2个数组,用来记录太平洋流向大西洋,大西洋流向太平洋的水流
1
2
3
4
5
6
7
8
9// 赋值
this.row = heights.length;
this.col = heights[0].length;
this.heights = heights;
// 记录太平洋流向大西洋水流的数组
boolean[][] pacificOceanToAtlantic = new boolean[row][col];
// 记录大西洋流向太平洋水流的数组
boolean[][] atlanticOceanToPacific = new boolean[row][col];构建4个循环,分别从行列出发来递归出太平洋,大西洋往对方流向的水流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 从行出发,固定住列,求出行方向流向大西洋的水流
for (int i = 0; i < row; i++) {
dfs(i, 0, pacificOceanToAtlantic);
}
// 从列出发,固定住行,求出列方向流向大西洋的水流
for (int i = 0; i < col; i++) {
dfs(0, i, pacificOceanToAtlantic);
}
// 然后从行出发,固定住列,求出大西洋流向太平洋的水流,因为从右下角开始遍历,所以col - 1
for (int i = 0; i < row; i++) {
dfs(i, col - 1, atlanticOceanToPacific);
}
// 然后从列出发,固定住行,求出大西洋流向太平洋的水流,因为从右下角开始遍历,所以row - 1
for (int i = 0; i < col - 1; i++) {
dfs(row - 1, i, atlanticOceanToPacific);
}然后合并得到结果
1
2
3
4
5
6
7
8
9
10
11// 遍历数组的所有元素,做交集,输出2个数组中共同的值
List<List<Integer>> ans = new ArrayList<>();
for (int i = 0; i < pacificOceanToAtlantic.length; i++) {
for (int j = 0; j < pacificOceanToAtlantic[i].length; j++) {
// 取得交集
if(pacificOceanToAtlantic[i][j] && atlanticOceanToPacific[i][j]) {
ans.add(Arrays.asList(i, j));
}
}
}
return ans;最后写出递归相关代码,这里需要定义个值,方向值,当然,这个其实可以移动到成员变量上去
1
2// 4个方向,上下左右
int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};然后在各个方向上判断水流方向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 如果这块高度被之前判断过了,直接跳过
if(oceans[row][col]) {
return;
}
// 然后将这块标记为true,即表示这块肯定被流过
oceans[row][col] = true;
for (int[] direction : directions) {
// 计算新的方向,循环4次方位,得到新的方向
int newRow = row + direction[0];
int newCol = col + direction[1];
// 判断这个方向是否比当前高度更高,如果是,则去这个方向进行遍历(因为如果目标方向比当前更高,表示当前的水流一定是从目标方向流过来的)
// 当然新的方向如果和当前方向高度相同,只是表示水流会互流(水可能从旁边流过来,也可能从这个高度流过去),也就是当在新的高度判断时,当判断到当前高度
// 与新的高度相同,也会回到当前高度重新判断,但是由于当前高度已经被标记为True,那么它会被直接return掉
if(newRow >= 0 && newRow < this.row && newCol >= 0 && newCol < this.col && this.heights[newRow][newCol] >= this.heights[row][col]) {
dfs(newRow, newCol, oceans);
}
}
模拟过程
附上完整代码
1 | class Solution { |