1.最小路径和(力扣LCR 99题)
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:一个机器人每次只能向下或者向右移动一步。
方法一:暴力递归
//暴力递归public static int minPathSum(int[][] grid){return f1(grid,grid.length-1,grid[0].length-1);}public static int f1(int[][] grid,int i,int j){if(i==0 && j==0){return grid[0][0];}int up=Integer.MAX_VALUE;//从上边的格子移动过来的int left=Integer.MAX_VALUE;//从左边的格子移动过来的if(i-1>=0){//判断不越界up=f1(grid,i-1,j);}if(j-1>=0){//判断不越界left=f1(grid,i,j-1);}return grid[i][j]+Math.min(up,left);}
暴力递归思路很清晰但是时间复杂度会非常高。提交也会超时。
因为此暴力递归中,存在大量的重复计算,因此,时间复杂度比较差。
方法二:记忆化递归
带有记忆化的递归,也就是将每次的计算结果存入到一个结构中,每次发生重复计算时,直接从存数据的结构中取数据即可,由此可以减少大量的重复计算,时间复杂度有了较大的优化。
class Solution {public static int minPathSum(int[][] grid){int n=grid.length;int m=grid[0].length;int[][] dp=new int[n][m];for(int i=0;i<n;i++){for(int j=0;j<m;j++){dp[i][j]=-1;}}return f2(grid,grid.length-1,grid[0].length-1,dp);}public static int f2(int[][] grid, int i, int j, int[][] dp) {if(dp[i][j]!=-1){return dp[i][j];}int ans;if(i==0 && j==0){ans=grid[0][0];}else {int up=Integer.MAX_VALUE;//从上边的格子移动过来的int left=Integer.MAX_VALUE;//从左边的格子移动过来的if(i-1>=0){//判断不越界up=f2(grid,i-1,j,dp);}if(j-1>=0){//判断不越界left=f2(grid,i,j-1,dp);}ans=grid[i][j]+Math.min(up,left);}dp[i][j]=ans;return ans;}
}
方法三:根据依赖关系推出
每一个位置,只能从上或者左得到,只依赖上和左。
class Solution {//非递归,按照依赖关系求解public static int minPathSum(int[][] grid){int n=grid.length;int m=grid[0].length;int[][] dp=new int[n][m];dp[0][0]=grid[0][0];for(int i=1;i<n;i++){dp[i][0]=dp[i-1][0]+grid[i][0];}for(int j=1;j<m;j++){dp[0][j]=dp[0][j-1]+grid[0][j];}for(int i=1;i<n;i++){for(int j=1;j<m;j++){dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];}}return dp[n-1][m-1];}
}
方法四:依赖关系+空间压缩
我们有时候没有必要,为了求二维数组中的某一个值而弄一个二维数组,如果压缩为一维数组,也能够解决问题的话,那么我们应该尽量采用一维数组的形式。
我们用一维数组来表示第i行的结果,直到推到最后一行。
class Solution {//位置依赖+空间压缩public static int minPathSum(int[][] grid){int n=grid.length;int m=grid[0].length;int[] dp=new int[m];dp[0]=grid[0][0];for(int j=1;j<m;j++){dp[j]=dp[j-1]+grid[0][j];}for(int i=1;i<n;i++){dp[0]+=grid[i][0];for(int j=1;j<m;j++){dp[j]=Math.min(dp[j-1],dp[j])+grid[i][j];}}return dp[m-1];}
}
2.单词搜索(力扣79题)
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
暴力递归搜索
首先遍历二维数组,看当前字符是否是要查找单词的首字母,如果是,调用递归,否则跳过。
然后就是递归过程,每次递归,都要向四个方向去搜索剩余字母,分别判断符合条件并调用相应的递归函数。
class Solution {public boolean exist(char[][] board, String word) {char[] charArray = word.toCharArray();boolean[][] flag=new boolean[board.length][board[0].length];for(int i=0;i<board.length;i++){for(int j=0;j<board[0].length;j++){if(board[i][j]==charArray[0]){flag[i][j]=true;if(f1(board,charArray,i,j,flag,0)){return true;}flag[i][j]=false;}}}return false;}public boolean f1(char[][] board,char[] charArray,int i,int j,boolean[][] flag,int index){index++;if(index>=charArray.length){return true;}boolean isFait=false;if(i-1>=0 && !flag[i-1][j] && charArray[index]==board[i-1][j]){flag[i-1][j]=true;isFait=f1(board,charArray,i-1,j,flag,index) || isFait;flag[i-1][j]=false;}if(i+1<board.length && !flag[i+1][j] && charArray[index]==board[i+1][j]){flag[i+1][j]=true;isFait=f1(board,charArray,i+1,j,flag,index) || isFait;flag[i+1][j]=false;}if(j-1>=0 && !flag[i][j-1] && charArray[index]==board[i][j-1]){flag[i][j-1]=true;isFait=f1(board,charArray,i,j-1,flag,index) || isFait;flag[i][j-1]=false;}if(j+1<board[0].length && !flag[i][j+1] && charArray[index]==board[i][j+1]){flag[i][j+1]=true;isFait=f1(board,charArray,i,j+1,flag,index) || isFait;flag[i][j+1]=false;}return isFait;}
}
3.最长公共子序列(力扣LCR 95题)
给定两个字符串 text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
- 例如,
"ace"
是"abcde"
的子序列,但"aec"
不是"abcde"
的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。