前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >leetcode-太平洋大西洋水流问题

leetcode-太平洋大西洋水流问题

作者头像
程序员小王
发布2019-08-23 16:08:18
6150
发布2019-08-23 16:08:18
举报
文章被收录于专栏:架构说

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

信息卡片

  • 时间:2019-08-16
  • 题目链接:https://leetcode-cn.com/problems/pacific-atlantic-water-flow
  • tag:DP,BackTracking

题目描述

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。

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

规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动

请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

提示:

输出坐标的顺序不重要

m 和 n 都小于150

测试用例

代码语言:javascript
复制
给定下面的 5x5 矩阵:

  太平洋 ~   ~   ~   ~   ~ 
       ~  1   2   2   3  (5) *
       ~  3   2   3  (4) (4) *
       ~  2   4  (5)  3   1  *
       ~ (6) (7)  1   4   5  *
       ~ (5)  1   1   2   4  *
          *   *   *   *   * 大西洋

返回:

[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (上图中带括号的单元).

题目理解

水流既可以流动到“太平洋"意思是:从任意位置出发,能达到 大陆的左边界和上边界 就可以。(~ ~ ~)

水流动到“大西洋” 意思是:从任意位置出发,能达到大陆的右边界和下边界(* * * *).

要求是:请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

Solution 1 无状态回溯法
  • 拦路虎:

当自己没有思路,陷入困局时候,我做的事情是把问题描述出来, 因为什么原因,导致你无法写出代码,思路中断。 衡量标准:自己描述问题是否清楚

  1. 如何判断一个点同时既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标件, 需要2个返回结果,我就迷惑了,不知道如何下手了,因为以前只要返回一个结果就可以来 true/false? 规定
  • 水不能流动 ,返回 0;
  • 水流可以流动到太平洋 返回1
  • 水流可以流动到 大西洋 返回2
  • 水流既可以流动到“太平洋”,又能流动到“大西洋” 返回 3 说明:3=2|1,按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1
  1. 既然是动态规划,需要dp记录历史过程,来优化,但是这个题目我缺用不到,因此这个题目无法实现了? 那就不优化,高到低或者在同等高度上流动。 从高到低 只有一个方向 ,但是同等高度如何判断 10--10 |10-10 死循环了。 因访问过记录,采用最大值INT_MAX防止死循环。
  2. 缺点 当n=13时候,超出时间限制 时间复杂度 指数级别 4 (m*n) 肯定超时。 维基百科 回溯法 回溯法(英语:backtracking)是暴力搜索法中的一种。

在最坏的情况下,回溯法会导致一次复杂度为指数时间的计算。

Solution 2 回溯法

方法1 和方法3 都是采用回溯法 ,但是思路不一样

方法1 是从每个元素 寻找到顶点,重复计算部分无法重复利用

方法2 和顶点 太平洋,大西洋连同的元素,重复计算部分 可以重复利用。

  • 拦路虎:

当自己没有思路,陷入困局时候,我做的事情是把问题描述出来, 因为什么原因,导致你无法写出代码,思路中断。 衡量标准:自己描述问题是否清楚

  1. 如何统计矩阵中流向 太平洋 的坐标,以前方式 从(0,0)到(m,n)?
  2. 如何统计矩阵中流向 大西洋 的坐标,以前方式 从(0,0)到(m,n)??
  • 正确思路

总体思路还是回溯,我们对能够流入太平洋的(第一行和第一列)开始进行上下左右探测。 同样我们对能够流入大西洋的(最后一行和最后一列)开始进行上下左右探测。 最后将探测结果进行合并即可。 合并的条件就是当前单元既能流入太平洋又能流入大西洋

-

参考代码

  • Solution 1 c++
代码语言:javascript
复制
//超时
class Solution2 {
public:
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& matrix) 
    {
         vector<vector<int>> result;
         int rows = matrix.size();
         if ( rows  == 0) return result;//不要忘记边界检查
         int cols =matrix[0].size();

         vector<int> temp(2);

        for (int i=0; i < rows; i++ )
        {
            for (int j=0;j < cols; j++)
            {
                if ( dfs(matrix,i,j,INT_MAX,rows,cols) ==3)
                {
                    temp[0]=i;
                    temp[1]=j;
                    result.push_back(temp);
                }
            }
        }

        return result;

    }
    //从中心到周围遍历时候 需考虑死循环,matrix[row][col]=INT_MAX别人来回运动
    int dfs(vector<vector<int>>& matrix,int row,int col,int pre,int rows,int cols)
    {

      if (row < 0 || col < 0) return 1; //边界检查-遇到太平洋
      if (row >=rows || col >= cols ) return 2; //边界检查-遇到大西洋

      //当前节点判断逻辑  
      if (matrix[row][col] > pre)  return 0;  //不能流动 返回0
      pre =matrix[row][col];

      //依赖四个节点必须完全遍历完毕,而不是只要其从一个可以通过就可以 
      matrix[row][col]=INT_MAX;  
      int isBoth =  dfs(matrix,row,col-1,pre,rows,cols) |  
                     dfs(matrix,row,col+1,pre,rows,cols) | 
                     dfs(matrix,row+1,col,pre,rows,cols) | 
                     dfs(matrix,row-1,col,pre,rows,cols) ;
     //cout<< row <<":"<<cols<<":"<<pre <<":"<<isBoth <<endl;
      matrix[row][col] = pre;//破坏当前节点数据,后面其他节点无法在使用该节点
      return isBoth;


    }
};
  • Solution 2 回溯

c++

代码语言:javascript
复制
class Solution {
public:
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& matrix) {
        vector<vector<int>> out;
        int row = matrix.size();
        if ( 0 == row ) return out;
        int col = matrix[0].size();
        if (0 == col) return out;

        //能流动到“太平洋"的陆地
        vector<vector<bool>> dp1(row, vector<bool>(col, false));
        //能流动到“大西洋"的陆地
        vector<vector<bool>> dp2(row, vector<bool>(col, false));

        //从第一行/最后一行出发寻找连同节点,不变的x坐标
        for (int j = 0; j < col; j++) {
            dfs(0,j,INT_MIN,matrix,dp1);
            dfs(row-1,j,INT_MIN,matrix,dp2);
        }
        //从第一列/最后一列出发寻找连同节点,不变的y坐标
        for (int i = 0; i < row; i++) {
             dfs(i,0,INT_MIN,matrix,dp1);
             dfs(i,col-1,INT_MIN,matrix,dp2);
        }

        vector<int> temp(2);
        for (int i=0;i < row;i++)
        {
            for (int j=0; j < col;j++)
            {   
                //请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。
                if (dp1[i][j] == true && dp2[i][j] ==true)
                {
                    temp[0] = i;
                    temp[1] = j;
                    out.push_back(temp);
                }
            }
        }
        return out;

    }

    void dfs(int row,int col, int height, vector<vector<int>>& matrix,vector<vector<bool>>& visited)
   {
        if ( row < 0 || row >= matrix.size() ||
             col < 0 || col >= matrix[0].size() 
           ) 
        {
            return ; //农村包围城市,农村就边界
        }

         if (visited[row][col] == true) 
         {
             return ;//已经dfs遍历过
         }
        if (height > matrix[row][col])
        {
            return ;//不是升序,此路不通
        }  

        visited[row][col]  =true;//能走到这里说明 当前层次遍历完毕

        //每个点扩散四周,每个点被四周扩散时候,只要满足一个就可以连通,减少扩散层次。
        dfs(row+1,col, matrix[row][col],matrix,visited);
        dfs(row-1,col, matrix[row][col],matrix,visited);
        dfs(row,col+1, matrix[row][col],matrix,visited);
        dfs(row,col-1, matrix[row][col],matrix,visited);
   }
};

js

代码语言:javascript
复制
function dfs(i, j, height, m, matrix, rows, cols) {
  if (i >= rows || i < 0) return;
  if (j >= cols || j < 0) return;

  if (matrix[i][j] < height) return;

  if (m[i][j] === true) return;

  m[i][j] = true;

  dfs(i + 1, j, matrix[i][j], m, matrix, rows, cols);
  dfs(i - 1, j, matrix[i][j], m, matrix, rows, cols);
  dfs(i, j + 1, matrix[i][j], m, matrix, rows, cols);
  dfs(i, j - 1, matrix[i][j], m, matrix, rows, cols);
}
/**
 * @param {number[][]} matrix
 * @return {number[][]}
 */
var pacificAtlantic = function(matrix) {
  const rows = matrix.length;
  if (rows === 0) return [];
  const cols = matrix[0].length;
  const pacific = Array.from({ length: rows }, () => Array(cols).fill(false));
  const atlantic = Array.from({ length: rows }, () => Array(cols).fill(false));
  const res = [];

  for (let i = 0; i < rows; i++) {
    dfs(i, 0, 0, pacific, matrix, rows, cols);
    dfs(i, cols - 1, 0, atlantic, matrix, rows, cols);
  }

  for (let i = 0; i < cols; i++) {
    dfs(0, i, 0, pacific, matrix, rows, cols);
    dfs(rows - 1, i, 0, atlantic, matrix, rows, cols);
  }

  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
      if (pacific[i][j] === true && atlantic[i][j] === true) res.push([i, j]);
    }
  }

  return res;
};

题目总结

动态规划:从小到大,根据已知推算为止。方向是确定的,计算过数据可以重复利用

tree递归:是从大到小,未知依赖已知。方向是确定的

Bfs:是从大到小,未知依赖已知。方向是边,也是确定的

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Offer多多 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 每日一题 太平洋大西洋水流问题
    • 信息卡片
      • 题目描述
        • 测试用例
          • 题目理解
            • Solution 1 无状态回溯法
            • Solution 2 回溯法
          • 参考代码
            • 题目总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档