LeetCode 329. 矩阵中的最长递增路径 (2种方法)

本文解析了LeetCode问题329中矩阵最长递增路径的两种优化解法:一是利用记忆化搜索(DFS)减少重复计算,二是采用拓扑排序的思想,通过计算每个节点的出度来寻找最长递增序列。通过对比和代码实现,展示了如何提高搜索效率并达到AC解决方案。
摘要由CSDN通过智能技术生成

题目地址:leetcode329 矩阵中的最长递增路径

题意:
        给定一个整数矩阵,找出最长递增路径的长度。
对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

解法1 : 首先第一个想到的是用dfs搜索,从每一个点出发,每次搜索会有一个最大递增长度,然后取max即可。

代码:

class Solution {
public:
    int maxlen=0;
    int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
    int dfs(vector<vector<int>>& matrix,int x,int y,int n,int m)
    {
        int mx=0;
        for(int i=0;i<4;i++)
        {
            int tx=x+dir[i][0];
            int ty=y+dir[i][1];
            if(tx<0||tx>=n||ty<0||ty>=m)continue;
            if(matrix[tx][ty]>matrix[x][y])
            {
                mx=max(mx,dfs(matrix,tx,ty,n,m)+1);
            }
        }
        return mx;
    }
    int longestIncreasingPath(vector<vector<int>>& matrix) {

        int len=matrix.size();
        if(len==0)return 0;

        for(int i=0;i<len;i++)
        {
            for(int j=0;j<matrix[i].size();j++)
            {
                maxlen=max(maxlen,dfs(matrix,i,j,len,matrix[i].size())+1);
            }
        }

        return maxlen;
    }
};

提交发现,只通过了135/138 ,时间超出了题目限制

所以说暴力搜索不能解决全部的问题,我们思考记忆化去搜索来优化算法

如果我们能记录下当前位置的最大长度,那么只需要看看当前位置的最大长度是否已经计算出了,如果已经计算出了,我们就return,如果没有计算出来,那么我从1长度开始搜索,这样大大加快运行速度。

AC代码:

class Solution {
public:
    int maxlen=0;
    int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
    int mx[300][300];
    int dfs(vector<vector<int>>& matrix,int x,int y,int n,int m)
    {
        if(mx[x][y]!=0)
        {
            return mx[x][y];
        }
        mx[x][y]++;
        for(int i=0;i<4;i++)
        {
            int tx=x+dir[i][0];
            int ty=y+dir[i][1];
            if(tx<0||tx>=n||ty<0||ty>=m)continue;
            if(matrix[tx][ty]>matrix[x][y])
            {
                mx[x][y]=max(mx[x][y],dfs(matrix,tx,ty,n,m)+1);
            }
        }
        return mx[x][y];
    }
    int longestIncreasingPath(vector<vector<int>>& matrix) {

        int len=matrix.size();
        if(len==0)return 0;

        for(int i=0;i<len;i++)
        {
            for(int j=0;j<matrix[i].size();j++)
            {
                maxlen=max(maxlen,dfs(matrix,i,j,len,matrix[i].size()));
            }
        }
        return maxlen;
    }
};

对比2个代码,区别就是有没有记忆化,第二种采用二维数组去记录每个点的最大递增路径,大大减少了搜索的次数。

方法2: 也是官方的一种奇妙的解法: 拓扑排序

我们可以把图当做一个有向图来看,当前节点(i,j)四个方向满足递增的序列,那么 (i,j)这个点的出度++ 。

看下面一个例子

[[1,2]]
[[2,3]]

例如 i=0,j=0;

满足条件(指的的递增这个条件)的话有 点(0,1) 点(1,0)

所以说 out[0][0]=2 // out 表示出度

由于所有出度为0的点,都是最长递增序列的最后一个点,
我们可以凭借出度来进行拓扑排序。 那么进行了几轮拓扑排序,就是我们要的答案,也就是最长递增子序列

AC代码:

class Solution {
public:
    int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
    int longestIncreasingPath(vector<vector<int>>& matrix) {

        int n=matrix.size();
        if(n==0)return 0;
        int m=matrix[0].size();
        

        auto o=vector<vector<int>>(n,vector<int>(m));

        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                for(int k=0;k<4;k++)
                {
                    int tx=i+dir[k][0];
                    int ty=j+dir[k][1];
                    if(tx<0||tx>=n||ty<0||ty>=m)continue;
                    if(matrix[tx][ty]>matrix[i][j])
                    {
                        ++o[i][j];
                    }
                }
            }
        }
        queue<pair<int,int>>q;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(o[i][j]==0)
                {
                    q.push({i,j});
                }
            }
        }
        int ans=0;

        while(!q.empty())
        {
            int siz=q.size();
            ans++;
            while(siz--)
            {
                auto x=q.front();
                q.pop();

                for(int i=0;i<4;i++)
                {
                    int tx=x.first+dir[i][0];
                    int ty=x.second+dir[i][1];
                    if(tx<0||tx>=n||ty<0||ty>=m||matrix[tx][ty]>=matrix[x.first][x.second])continue;
                    o[tx][ty]--;
                    if(o[tx][ty]==0)
                    {
                        q.push({tx,ty});
                    }
                }
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值