给定一个 m x n 整数矩阵 matrix ,找出其中 最长递增路径 的长度。
对于每个单元格,你可以往上,下,左,右四个方向移动。 你 不能 在 对角线 方向上移动或移动到 边界外(即不允许环绕)。
题解
方法:记忆化深度优先搜索
将矩阵看成一个有向图,每个单元格对应图中的一个节点,如果相邻的两个单元格的值不相等,则在相邻的两个单元格之间存在一条从较小值指向较大值的有向边。问题转化成在有向图中寻找最长路径。
深度优先搜索是非常直观的方法。从一个单元格开始进行深度优先搜索,即可找到从该单元格开始的最长递增路径。对每个单元格分别进行深度优先搜索之后,即可得到矩阵中的最长递增路径的长度。
但是如果使用朴素深度优先搜索,时间复杂度是指数级,会超出时间限制,因此必须加以优化。
朴素深度优先搜索的时间复杂度过高的原因是进行了大量的重复计算,同一个单元格会被访问多次,每次访问都要重新计算。由于同一个单元格对应的最长递增路径的长度是固定不变的,因此可以使用记忆化的方法进行优化。用矩阵 memo 作为缓存矩阵,已经计算过的单元格的结果存储到缓存矩阵中。
使用记忆化深度优先搜索,当访问到一个单元格 (i,j) 时,如果 memo[i][j] =0,说明该单元格的结果已经计算过,则直接从缓存中读取结果,如果
memo[i][j]=0,说明该单元格的结果尚未被计算过,则进行搜索,并将计算得到的结果存入缓存中。
遍历完矩阵中的所有单元格之后,即可得到矩阵中的最长递增路径的长度。
class Solution {
public int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public int rows, columns;
public int longestIncreasingPath(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return 0;
}
rows = matrix.length;
columns = matrix[0].length;
int[][] memo = new int[rows][columns];
int ans = 0 ;
for(int i=0;i<rows;i++){
for(int j=0;j<columns;j++){
ans=Math.max(ans,dfs(matrix,memo,i,j));
}
}
return ans;
}
public int dfs(int[][] matrix, int[][] memo, int i, int j){
if(memo[i][j]!=0){
return memo[i][j];
}
//最短的递增路径值为1
++memo[i][j];
for(int[] dir:dirs){
int nextX=i+dir[0];
int nextY=j+dir[1];
if(inArea(matrix,nextX,nextY)==true&&matrix[nextX][nextY]>matrix[i][j]){
memo[i][j] = Math.max(memo[i][j],dfs(matrix,memo,nextX,nextY)+1);
}
}
return memo[i][j];
}
public boolean inArea(int[][] matrix,int i, int j){
if(i<matrix.length&&i>=0&&j<matrix[0].length&&j>=0){
return true;
}
return false;
}
}
注
对于代码,为什么是O(mn)不是O(mn)*O(dfs的时间复杂度)?
for(int i=0;i<rows;i++){
for(int j=0;j<columns;j++){
ans=Math.max(ans,dfs(matrix,memo,i,j));
}
}