每日一题—— 太平洋大西洋水流问题

简介: 每日一题—— 太平洋大西洋水流问题

417. 太平洋大西洋水流问题

问题描述

有一个 m × n 的矩形岛屿,与 太平洋 和 大西洋 相邻。

“太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。

这个岛被分割成一个由若干方形单元格组成的网格。

给定一个 m x n 的整数矩阵 heights , heights[r][c] 表示坐标 (r, c) 上单元格 高于海平面的高度 。

岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。

返回 网格坐标 result 的 2D列表 ,其中 result[i] = [ri, ci] 表示雨水可以从单元格 (ri, ci) 流向 太平洋和大西洋 。

简单描述一下题目,雨水只能从高的地方流向低的地方,求二维表格中哪些坐标的雨水既可以流入太平洋又可以流入大西洋。

思路

整理题意,需要我们统计能够同时流向两片海域的格子。

从源点(格子)流向汇点(海域)是按照高度从高到低(非严格)的规则,那么反过来从海域到格子则是按照从低到高(非严格)规则进行,同时本身处于边缘的格子与海域联通。

因此我们可以使用两遍 DFS/BFS 进行求解:分别从与当前海域直接相连的边缘格子出发,统计能够流向当前海域的格子集合,两片海域求得的集合交集即是答案。

题解

BFS(多源 BFS)

使用 BFS 进行求解:目的是构造出两个记录矩阵 visited1 和 resvisited2 记录的节点是否遍历过,因为有if判断,所以只要遍历过,一定是可以流向海洋1 or 海洋2, 起始将所有与海域相连的格子放入队列,然后跑一遍 BFS ,所有能够进入队列的格子均能够与海域联通。

最后统计所有满足 visited1[i][j]=visited2[i][j]=true 的格子即是答案。

代码:

// 边界长度,m,n
var m, n = 0, 0
var g [][]int
func pacificAtlantic(heights [][]int) [][]int {
  g = heights
  // 行
  m = len(g)
  // 列
  n = len(g[0])
  // 用于存放可以到海洋1、海洋2 的格子位置
  var d1, d2 [][]int
  var visited1, visited2 = make([][]bool, m), make([][]bool, m)
  // 初始化 d1,d2 visited1,visited2
  // 因为最外围那一圈格子是直接与海洋1或海洋2相连的 直接标记即可
  for i := 0; i < m; i++ {
    visited1[i] = make([]bool, n)
    visited2[i] = make([]bool, n)
    for j := 0; j < n; j++ {
      // 顶部的一行或者左边的一列
      // 可流入海洋1 更新visited1对应值,将坐标(i,j)加入d1中
      if i == 0 || j == 0 {
        visited1[i][j] = true
        d1 = append(d1, []int{i, j})
      }
      // 底部的那一行和右边的那一列
      // 可以流入海洋2,更新visited2对应值,将坐标(i,j)加入d2中
      if i == m-1 || j == n-1 {
        visited2[i][j] = true
        d2 = append(d2, []int{i, j})
      }
    }
  }
  // 对初始化后的d1的每个点开始进行广度优先搜索
  bfs(&d1, visited1)
  // 对初始化后的d2的每个点开始进行广度优先搜索
  bfs(&d2, visited2)
  var ans [][]int
  // 取并集
  for i := 0; i < m; i++ {
    for j := 0; j < n; j++ {
      // 既可以流入海洋1,也可以流入海洋2
      if visited1[i][j] && visited2[i][j] {
        ans = append(ans, []int{i, j})
      }
    }
  }
  return ans
}
// 分别代表从当前位置(i,j)向下、上、右、左移动(看上面的图就明白了)
var directs = [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
// 广度优先搜索,从初始化后的d1和d2的每个点开始进行广度优先搜索
// d1:  [[0 0] [0 1] [0 2] [0 3] [0 4] [1 0] [2 0] [3 0] [4 0]]
// d2:  [[0 4] [1 4] [2 4] [3 4] [4 0] [4 1] [4 2] [4 3] [4 4]]
func bfs(d *[][]int, visited [][]bool) {
  for len(*d) != 0 {
    info := (*d)[0]
    x := info[0]
    y := info[1]
    t := g[x][y]
    // 对(x,y)进行移动 方向就是上下左右,nx,ny就是移动后的坐标
    for _, direct := range directs {
      nx := x + direct[0]
      ny := y + direct[1]
      // 检查移动后 是否越界
      if nx < 0 || nx >= m || ny < 0 || ny >= n {
        continue
      }
      // 检查该点是否可以到海洋x(你让bfs的是d1和visited1 这里就是海洋1,同样...就是海洋2)
      // visited[nx][ny]: 移动后的格子是否遍历过,并且可以进入海洋x
      // g[nx][ny]<t: 移动后的位置是否比移动前的位置低,如果低的话,就不满足题意
      if visited[nx][ny] || g[nx][ny] < t {
        continue
      }
      // 如果上面两个检查都通过了,说明符合要去
      // 加入到对应切片d和更新visited对应值即可
      *d = append(*d, []int{nx, ny})
      visited[nx][ny] = true
    }
    *d = (*d)[1:]
  }
}

提交结果:

DFS:

道理同BFS

代码:

// 该题是一个二维平面回溯算法的题目
// 边界长度,m
var m, n = 0, 0
var g [][]int
func pacificAtlantic(heights [][]int) [][]int {
  g = heights
  // 行
  m = len(g)
  // 列
  n = len(g[0])
  // 记录的节点是否遍历过,因为有if判断,所以只要遍历过,一定是可以流向海洋1 or 海洋2
  var visited1, visited2 = make([][]bool, m), make([][]bool, m)
  for i := 0; i < m; i++ {
    visited1[i] = make([]bool, n)
    visited2[i] = make([]bool, n)
  }
  // 第一个点(0,0)开始 进行深度优先搜索
  for i := 0; i < m; i++ {
    for j := 0; j < n; j++ {
      // 顶部的一行或者左边的一列,可以流入海洋1
      if i == 0 || j == 0 {
        // 如果没有遍历过,进行深度优先搜索
        if !visited1[i][j] {
          dfs(i, j, visited1)
        }
      }
      // 底部的那一行和右边的那一列,可以流入海洋2
      if i == m-1 || j == n-1 {
        // 如果没有遍历过,进行深度优先搜索
        if !visited2[i][j] {
          dfs(i, j, visited2)
        }
      }
    }
  }
  var ans [][]int
  // 取并集
  for i := 0; i < m; i++ {
    for j := 0; j < n; j++ {
      // 既可以流入海洋1,也可以流入海洋2
      if visited1[i][j] && visited2[i][j] {
        ans = append(ans, []int{i, j})
      }
    }
  }
  return ans
}
// 分别代表从当前位置(i,j)向下、上、右、左移动(看上面的图就明白了)
var directs = [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
func dfs(x, y int, visited [][]bool) {
  visited[x][y] = true
  for _, direct := range directs {
    nx := x + direct[0]
    ny := y + direct[1]
    // 检查移动后 是否越界
    if nx < 0 || nx >= m || ny < 0 || ny >= n {
      continue
    }
    // 检查该点是否可以到海洋x (你让bfs的是d1和visited1 这里就是海洋1,同样...就是海洋2)
    // visited[nx][ny]: 移动后的格子是否遍历过
    // g[nx][ny]<t: 移动后的位置是否比移动前的位置低,如果低的话,就不满足题意
    if visited[nx][ny] || g[nx][ny] < g[x][y] {
      continue
    }
    // 如果(nx,ny)满足条件,就继续深度优先搜索
    dfs(nx, ny, visited)
  }
}

提交结果:

相关文章
|
6月前
|
机器学习/深度学习 并行计算 定位技术
NYOJ22-吝啬的国度
NYOJ22-吝啬的国度
32 0
|
Cloud Native
【刷题日记】417. 太平洋大西洋水流问题
本次刷题日记的第 45 篇,力扣题为:417. 太平洋大西洋水流问题 ,中等
|
6月前
leetcode-417:太平洋大西洋水流问题
leetcode-417:太平洋大西洋水流问题
33 0
|
存储 人工智能 弹性计算
600天,我们在沙漠筑“城堡”
600天,我们在沙漠筑“城堡”
117 0
LeetCode——417. 太平洋大西洋水流问题
LeetCode——417. 太平洋大西洋水流问题
140 0
LeetCode——417. 太平洋大西洋水流问题
LeetCode每日一题——417. 太平洋大西洋水流问题
有一个 m × n 的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。
108 0
LeetCode每日一题——417. 太平洋大西洋水流问题
LeetCode 417. 太平洋大西洋水流问题
LeetCode 417. 太平洋大西洋水流问题
64 0
|
算法
算法:奶牛慢跑
题目: 奶牛们又出去锻炼蹄子去了! 有 N 头奶牛在无限长的单行道上慢跑。 每头奶牛在跑道上开始奔跑的位置都不相同,一些奶牛的奔跑速度也可能不同。
111 0
【LeetCode417】太平洋大西洋水流问题
(1)找出从太平洋出发的水所能到达的点:
127 0
【LeetCode417】太平洋大西洋水流问题