算法总结(12)--dfs, bfs记忆化, 减少不必要的搜索

矩阵寻路与水流算法
本文探讨了两个经典的矩阵算法问题:寻找最长递增路径和判断哪些网格单元的水能流向两个海洋。通过深度优先搜索(DFS)和广度优先搜索(BFS)的方法解决了这些问题,并讨论了如何避免重复搜索以提高效率。

在寻路这类题目时,对每一个位置的节点出发去遍历,很容易超时,需要想到是否重复搜素了,如何减少不必要的搜索


329. Longest Increasing Path in a Matrix

题目地址

https://leetcode.com/problems/longest-increasing-path-in-a-matrix/

题目描述

Given an integer matrix, find the length of the longest increasing path.

From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed).

Example 1:

nums = [
  [9,9,4],
  [6,6,8],
  [2,1,1]
]

Return 4
The longest increasing path is [1, 2, 6, 9].

Example 2:

nums = [
  [3,4,5],
  [3,2,6],
  [2,2,1]
]

Return 4
The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.

ac

需要理解 每个位置只需要遍历一次就够了,所以需要设置一个vis访问标识 ,避免重复的访问

ac代码如下

class Solution {
public:
    int m, n;

    int dfs(vector<vector<int>>& matrix, int x, int y, vector<vector<int>> &ansCeng)
    {

        int dir[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, {0,-1} };

        int cnt = 0;

        for (int i = 0; i < 4; i++)
        {
            int nx = x + dir[i][0];
            int ny = y + dir[i][1];

            if (!(nx >= 0 && nx < m && ny >= 0 && ny < n))
                continue;

            if (matrix[nx][ny] <= matrix[x][y])
                continue;

            cnt++;

            if (ansCeng[nx][ny] == -1)
            {
                ansCeng[x][y] = max(ansCeng[x][y], 1 + dfs(matrix, nx, ny, ansCeng)); // dfs不需要回溯
            }
            else{
                ansCeng[x][y] = max(ansCeng[x][y], 1 + ansCeng[nx][ny]);
            }

        }
        if (cnt == 0)
            ansCeng[x][y] = 1;
        return ansCeng[x][y];
    }

    int longestIncreasingPath(vector<vector<int>>& matrix) {
        m = matrix.size();
        if (m == 0)
            return 0;
        n = matrix[0].size();
        if (n == 0)
            return 0;

        vector<vector<int>> ansCeng = vector<vector<int>>(m, vector<int>(n, -1));

        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (ansCeng[i][j] == -1)
                    dfs(matrix, i, j, ansCeng);
            }
        }

        int maxH = 1;
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (ansCeng[i][j] > maxH)
                    maxH = ansCeng[i][j];
            }
        }

        return maxH;
    }
};

测试

void test1()
{
    int a[3][3] = {{9,9,4},{6,6,8},{2,1,1}};

    vector<vector<int>> v(3, vector<int>(3,-1));
    for(int i=0;i<3;i++)
    {
        for(int j =0;j<3;j++)
        {
            v[i][j] = a[i][j];
        }
    }

    Solution sol;
    cout << sol.longestIncreasingPath(v) << endl;
}

void test2()
{
    int a[3][3] = {{3,4,5},{3,2,6},{2,2,1}};

    vector<vector<int>> v(3, vector<int>(3,-1));
    for(int i=0;i<3;i++)
    {
        for(int j =0;j<3;j++)
        {
            v[i][j] = a[i][j];
        }
    }

    Solution sol;
    cout << sol.longestIncreasingPath(v) << endl;
}

417. Pacific Atlantic Water Flow

题目地址

https://leetcode.com/problems/pacific-atlantic-water-flow/

题目描述

Given an m x n matrix of non-negative integers representing the height of each unit cell in a continent, the “Pacific ocean” touches the left and top edges of the matrix and the “Atlantic ocean” touches the right and bottom edges.

Water can only flow in four directions (up, down, left, or right) from a cell to another one with height equal or lower.

Find the list of grid coordinates where water can flow to both the Pacific and Atlantic ocean.

Note:
The order of returned grid coordinates does not matter.
Both m and n are less than 150.
Example:

Given the following 5x5 matrix:

  Pacific ~   ~   ~   ~   ~ 
       ~  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  *
          *   *   *   *   * Atlantic

Return:

[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix).

ac

此题用dfs深度太大,直接超时。

采用bfs,每个点都作为出发点遍历,可以ac,不过耗时太多,因为有很多多于的遍历

从边缘出发,这样一遍后可以确定一些点(确定的是可以到达的点,这些点后面就不需要再进行遍历了)。

class Solution {
public:
    int m, n;
    vector<pair<int, int>> ans;

    // bfs,队列
    void flow(vector<vector<int>>& matrix, int i, int j, vector<vector<bool>> &pv)
    {
        vector<bool> vis = vector<bool>(m*n, false);
        queue<int> que;
        que.push(i*n + j);
        vis[i*n + j] = true;

        while (!que.empty())
        {
            int x = que.front() / n;
            int y = que.front() % n;
            que.pop();

            int dir[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };
            for (int k = 0; k < 4; k++)
            {
                int nx = x + dir[k][0];
                int ny = y + dir[k][1];
                if (!(nx >= 0 && nx < m && ny >= 0 && ny < n))
                    continue;
                // 高度比当前高度小的,或者已经是true,不需要访问了
                if (vis[nx*n + ny] || matrix[nx][ny] < matrix[x][y] || pv[nx][ny] == true)
                    continue;

                pv[nx][ny] = true;
                vis[nx*n + ny] = true;

                que.push(nx*n + ny);
            }
        }// end while
    }

    vector<pair<int, int>> pacificAtlantic(vector<vector<int>>& matrix) {
        m = matrix.size();
        if (m == 0)
            return ans;
        n = matrix[0].size();
        if (n == 0)
            return ans;
        vector<vector<bool>> Pacific;
        vector<vector<bool>> Atlantic;
        Pacific.resize(m, vector<bool>(n, false));
        Atlantic.resize(m, vector<bool>(n, false));
        for (int i = 0; i < m; i++){
            Pacific[i][0] = true;
            Atlantic[i][n - 1] = true;
        }
        for (int j = 0; j < n; j++)
        {
            Pacific[0][j] = true;
            Atlantic[m - 1][j] = true;
        }
        // bfs求解
        for (int i = 0; i < m; i++)
        {
            flow(matrix, i, 0, Pacific);
            flow(matrix, i, n - 1, Atlantic);
        }
        for (int j = 0; j < n; j++)
        {
            flow(matrix, 0, j, Pacific);
            flow(matrix, m-1, j, Atlantic);
        }
        // 存储结果
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (Pacific[i][j] && Atlantic[i][j])
                {
                    ans.push_back(pair<int, int>{i, j});
                }
            }
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值