<p align="center">
<a href="https://programmercarl.com/other/xunlianying.html" target="_blank">
  <img src="../pics/训练营.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>，贡献其他语言版本的代码，拥抱开源，让更多学习算法的小伙伴们收益！</strong></p>
# 417. 太平洋大西洋水流问题 

[题目链接](https://leetcode.cn/problems/pacific-atlantic-water-flow/)

## 思路

不少同学可能被这道题的题目描述迷惑了，其实就是找到哪些点 可以同时到达太平洋和大西洋。 流动的方式只能从高往低流。 

那么一个比较直白的想法，其实就是 遍历每个点，然后看这个点 能不能同时到达太平洋和大西洋。

至于遍历方式，可以用dfs，也可以用bfs，以下用dfs来举例。 

那么这种思路的实现代码如下：

```CPP 
class Solution {
private:
    int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};
    void dfs(vector<vector<int>>& heights, vector<vector<bool>>& visited, int x, int y) {
        if (visited[x][y]) return;

        visited[x][y] = true;

        for (int i = 0; i < 4; i++) {
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            if (nextx < 0 || nextx >= heights.size() || nexty < 0 || nexty >= heights[0].size()) continue;
            if (heights[x][y] < heights[nextx][nexty]) continue; // 高度不合适

            dfs (heights, visited, nextx, nexty);
        }
        return;
    }
    bool isResult(vector<vector<int>>& heights, int x, int y) {
        vector<vector<bool>> visited = vector<vector<bool>>(heights.size(), vector<bool>(heights[0].size(), false));

        // 深搜，将x,y出发 能到的节点都标记上。 
        dfs(heights, visited, x, y);
        bool isPacific = false;
        bool isAtlantic = false;

        // 以下就是判断x，y出发，是否到达太平洋和大西洋 
        for (int j = 0; j < heights[0].size(); j++) {
            if (visited[0][j]) {
                isPacific = true;
                break;
            }
        }
        for (int i = 0; i < heights.size(); i++) {
            if (visited[i][0]) {
                isPacific = true;
                break;
            }
        }
        for (int j = 0; j < heights[0].size(); j++) {
            if (visited[heights.size() - 1][j]) {
                isAtlantic = true;
                break;
            }
        }
        for (int i = 0; i < heights.size(); i++) {
            if (visited[i][heights[0].size() - 1]) {
                isAtlantic = true;
                break;
            }
        }
        if (isAtlantic && isPacific) return true;
        return false;
    }
public:

    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
        vector<vector<int>> result;
        // 遍历每一个点，看是否能同时到达太平洋和大西洋 
        for (int i = 0; i < heights.size(); i++) {
            for (int j = 0; j < heights[0].size(); j++) {
                if (isResult(heights, i, j)) result.push_back({i, j});
            }
        }
        return result;
    }
};

```

这种思路很直白，但很明显，以上代码超时了。 来看看时间复杂度。 

遍历每一个节点，是 m * n，遍历每一个节点的时候，都要做深搜，深搜的时间复杂度是： m * n 

那么整体时间复杂度 就是 O(m^2 * n^2) ，这是一个四次方的时间复杂度。 

## 优化 

那么我们可以 反过来想，从太平洋边上的节点 逆流而上，将遍历过的节点都标记上。 从大西洋的边上节点 逆流而长，讲遍历过的节点也标记上。 

从太平洋边上节点出发，如图：  

![图一](https://code-thinking-1253855093.file.myqcloud.com/pics/20220722103029.png)

从大西洋边上节点出发，如图：  

![图二](https://code-thinking-1253855093.file.myqcloud.com/pics/20220722103330.png)

按照这样的逻辑，就可以写出如下遍历代码：（详细注释）

（如果对dfs基础内容就不懂，建议看 [「代码随想录」DFS算法精讲！](https://leetcode.cn/problems/all-paths-from-source-to-target/solution/by-carlsun-2-66pf/)，还可以顺便解决 797. 所有可能的路径）

```CPP
class Solution {
private:
    int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1}; // 保存四个方向

    // 从低向高遍历，注意这里visited是引用，即可以改变传入的pacific和atlantic的值
    void dfs(vector<vector<int>>& heights, vector<vector<bool>>& visited, int x, int y) {
        if (visited[x][y]) return;
        visited[x][y] = true;
        for (int i = 0; i < 4; i++) { // 向四个方向遍历
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            // 超过边界
            if (nextx < 0 || nextx >= heights.size() || nexty < 0 || nexty >= heights[0].size()) continue;
            // 高度不合适，注意这里是从低向高判断
            if (heights[x][y] > heights[nextx][nexty]) continue;

            dfs (heights, visited, nextx, nexty);
        }
        return;

    }

public:

    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
        vector<vector<int>> result;
        int n = heights.size();
        int m = heights[0].size(); // 这里不用担心空指针，题目要求说了长宽都大于1

        // 记录从太平洋边出发，可以遍历的节点
        vector<vector<bool>> pacific = vector<vector<bool>>(n, vector<bool>(m, false));

        // 记录从大西洋出发，可以遍历的节点
        vector<vector<bool>> atlantic = vector<vector<bool>>(n, vector<bool>(m, false));

        // 从最上最下行的节点出发，向高处遍历
        for (int i = 0; i < n; i++) {
            dfs (heights, pacific, i, 0); // 遍历最上行，接触太平洋
            dfs (heights, atlantic, i, m - 1); // 遍历最下行，接触大西洋
        }

        // 从最左最右列的节点出发，向高处遍历
        for (int j = 0; j < m; j++) {
            dfs (heights, pacific, 0, j); // 遍历最左列，接触太平洋
            dfs (heights, atlantic, n - 1, j); // 遍历最右列，接触大西洋
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                // 如果这个节点，从太平洋和大西洋出发都遍历过，就是结果
                if (pacific[i][j] && atlantic[i][j]) result.push_back({i, j});
            }
        }
        return result;
    }
};

```

时间复杂度分析， 关于dfs函数搜索的过程 时间复杂度是 O(n * m)，这个大家比较容易想。 

关键看主函数，那么每次dfs的时候，上面还是有for循环的。 

第一个for循环，时间复杂度是：n * (n * m) 。

第二个for循环，时间复杂度是：m * (n * m)。 

所以本题看起来 时间复杂度好像是 ： n * (n * m) + m * (n * m) = (m * n) * (m + n)  。 

其实这是一个误区，大家再自己看 dfs函数的实现，其实 有visited函数记录 走过的节点，而走过的节点是不会再走第二次的。 

所以 调用dfs函数，**只要参数传入的是 数组pacific，那么地图中 每一个节点其实就遍历一次，无论你调用多少次**。 

同理，调用 dfs函数，只要 参数传入的是 数组atlantic，地图中每个节点也只会遍历一次。 

所以，以下这段代码的时间复杂度是 2 * n * m。 地图用每个节点就遍历了两次，参数传入pacific的时候遍历一次，参数传入atlantic的时候遍历一次。
```CPP 
// 从最上最下行的节点出发，向高处遍历
for (int i = 0; i < n; i++) {
    dfs (heights, pacific, i, 0); // 遍历最上行，接触太平洋
    dfs (heights, atlantic, i, m - 1); // 遍历最下行，接触大西洋
}

// 从最左最右列的节点出发，向高处遍历
for (int j = 0; j < m; j++) {
    dfs (heights, pacific, 0, j); // 遍历最左列，接触太平洋
    dfs (heights, atlantic, n - 1, j); // 遍历最右列，接触大西洋
}
``` 

那么本题整体的时间复杂度其实是：  2 * n * m + n * m ，所以最终时间复杂度为  O(n * m)  。

空间复杂度为：O(n * m) 这个就不难理解了。开了几个 n * m 的数组。 


## 其他语言版本


<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
  <img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a>
