welcome to my blog
LeetCode Top Interview Questions 329. Longest Increasing Path in a Matrix (Java版; Hard)
题目描述
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:
Input: nums =
[
[9,9,4],
[6,6,8],
[2,1,1]
]
Output: 4
Explanation: The longest increasing path is [1, 2, 6, 9].
Example 2:
Input: nums =
[
[3,4,5],
[3,2,6],
[2,2,1]
]
Output: 4
Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.
第一次做; 动态规划; dp的重点是按序进行; 使用匿名函数写堆的比较器; dp[i][j]表示以(i,j)结尾的最长路径; 这道题最好的方法是使用memo数据, 并不是dp
class Solution {
public int longestIncreasingPath(int[][] matrix) {
if(matrix==null || matrix.length==0 || matrix[0]==null || matrix[0].length==0)
return 0;
Queue<int[]> queue = new PriorityQueue<int[]>((a,b)->a[0]-b[0]);
int n = matrix.length, m = matrix[0].length;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
queue.add(new int[]{matrix[i][j], i, j});
}
}
int[][] dp = new int[n][m];
int max=0;
while(!queue.isEmpty()){
int[] cur = queue.poll();
int value = cur[0];
int i = cur[1];
int j = cur[2];
int curMax = 1;
if(i-1>=0 && value > matrix[i-1][j]){
curMax = Math.max(curMax, 1+dp[i-1][j]);
}
if(i+1<n && value > matrix[i+1][j]){
curMax = Math.max(curMax, 1+dp[i+1][j]);
}
if(j-1>=0 && value > matrix[i][j-1]){
curMax = Math.max(curMax, 1+dp[i][j-1]);
}
if(j+1<m && value > matrix[i][j+1]){
curMax = Math.max(curMax, 1+dp[i][j+1]);
}
dp[i][j] = curMax;
max = Math.max(max, curMax);
}
return max;
}
}
第一次做; 待优化的暴力递归, 使用memo数组记忆已经遍历过的位置, 拿空间换时间; 跟暴力递归的区别: 1)不再需要cur作为参数, 2)不再需要全局变量max; 最重要的是想清楚递归函数的递归逻辑
class Solution {
public int longestIncreasingPath(int[][] matrix) {
if(matrix==null || matrix.length==0 || matrix[0]==null || matrix[0].length==0)
return 0;
int n=matrix.length, m=matrix[0].length;
int[][] memo = new int[n][m];
int max = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
max = Math.max(max, core(memo,matrix, i, j));
}
}
return max;
}
public int core(int[][] memo, int[][] matrix, int i, int j){
if(memo[i][j] != 0)
return memo[i][j];
int max = 0;
if(isValid(matrix,i-1,j) && matrix[i-1][j] > matrix[i][j]){
max = Math.max(max, core(memo,matrix, i-1, j));
}
if(isValid(matrix,i+1,j)&&matrix[i+1][j]>matrix[i][j]){
max = Math.max(max, core(memo,matrix, i+1, j));
}
if(isValid(matrix,i,j-1)&&matrix[i][j-1]>matrix[i][j]){
max = Math.max(max, core(memo,matrix,i,j-1));
}
if(isValid(matrix,i,j+1)&&matrix[i][j+1]>matrix[i][j]){
max = Math.max(max, core(memo,matrix,i,j+1));
}
memo[i][j] = max+1;
return memo[i][j];
}
public boolean isValid(int[][] matrix, int i, int j){
int n=matrix.length, m=matrix[0].length;
return i>=0 && i<n && j>=0 && j<m;
}
}
第一次做; 暴力递归, 超时, 135 / 138; 关键: 什么时候更新max? 每次进入递归时更新max; cur是当前路径的长度; 四种选择是独立的, 不是互斥的!!
class Solution {
public int max = 0;
public int longestIncreasingPath(int[][] matrix) {
if(matrix==null || matrix.length==0 || matrix[0]==null || matrix[0].length==0)
return 0;
int n=matrix.length, m=matrix[0].length;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
core(matrix, i, j, 1);
}
}
return max;
}
public void core(int[][] matrix, int i, int j, int cur){
max = Math.max(max, cur);
if(isValid(matrix,i-1,j) && matrix[i-1][j] > matrix[i][j]){
core(matrix, i-1, j, cur+1);
}
if(isValid(matrix,i+1,j)&&matrix[i+1][j]>matrix[i][j]){
core(matrix, i+1, j, cur+1);
}
if(isValid(matrix,i,j-1)&&matrix[i][j-1]>matrix[i][j]){
core(matrix,i,j-1,cur+1);
}
if(isValid(matrix,i,j+1)&&matrix[i][j+1]>matrix[i][j]){
core(matrix,i,j+1, cur+1);
}
}
public boolean isValid(int[][] matrix, int i, int j){
int n=matrix.length, m=matrix[0].length;
return i>=0 && i<n && j>=0 && j<m;
}
}
动态规划题解; dp[i][j]表示以(i,j)结尾的最长路径; 比较器是用匿名函数写的; dp的重点是需要按序进行,保证大的dp可以依赖小的dp
看大家用dfs记忆搜索的比较多,这里提供一个纯dp的思路。先按照元素值大小进行排序,同时记录元素的值,x,y坐标。再以元素值从小到大的顺序在matrix矩阵中进行dp累积。
dp的重点是需要按序进行,保证大的dp可以依赖小的dp,在这里表现为,递增较大的元素需要在较小元素计算后再计算,所以使用最小堆弹出当前最小元素,并使用记录的x,y坐标到matrix矩阵上判断周边元素大小,完成dp累积
public int longestIncreasingPath(int[][] matrix) {
if(matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0)
return 0;
Queue<int[]> minDump = new PriorityQueue<int[]>((pre,next) -> pre[0] - next[0]);
for(int y = 0; y < matrix.length; y++){
for(int x = 0; x < matrix[0].length; x++){
minDump.offer(new int[]{matrix[y][x],y,x});
}
}
int[][] dp = new int[matrix.length][matrix[0].length];
int maxLength = 0;
while(minDump.size() > 0){
int[] curElement = minDump.poll();
int value = curElement[0];
int y = curElement[1];
int x = curElement[2];
int curMax = 1;
if(y > 0 && value > matrix[y - 1][x])
curMax = Math.max(curMax,dp[y - 1][x] + 1);
if(y < matrix.length - 1 && value > matrix[y + 1][x])
curMax = Math.max(curMax,dp[y + 1][x] + 1);
if(x > 0 && value > matrix[y][x - 1])
curMax = Math.max(curMax,dp[y][x - 1] + 1);
if(x < matrix[0].length - 1 && value > matrix[y][x + 1])
curMax = Math.max(curMax,dp[y][x + 1] + 1);
dp[y][x] = curMax;
maxLength = Math.max(maxLength,curMax);
}
return maxLength;
}
动态规划, 主要看看dp[i][j]的含义
力扣题解; 三种方法: 暴力递归; memo优化; 动态规划; 注意时间复杂度和空间复杂度的分析; 上下左右的遍历学一下, 别写4个if了