回溯法
剑指offer
什么事回溯法?
来自剑指offer解释:回溯法可以看成蛮力法的升级版,从解决问题每一步的所有可能选项里系统的选择一个可行的解决方案。回溯法适合由多个步骤组成的问题,并且每个步骤都有多个选项。当我们在某一步选择了其中一个选项时,就进入下一步,然后又面临新的选项。我们就这样重复选择,直至到达最终的状态。
用回溯法解决的问题的所有选项可以用树状结构表示。在某一步有n个可能的选项,那么该步骤可以看成是树状结构中的一个节点,每个选项看成树中节点连接线,经过这些连接线到达该节点的n个子节点。树的叶节点对应着终结状态。如果叶节点的状态满足题目的约束条件,那么我们找到了一个可行的解决方案。
如果在叶节点的状态不满足约束条件,那么只好回溯到它的上一个节点再尝试其他的选项。如果上一节点所有可能的选项都已经试过,并且不能到达满足约束条件的终结状态,则再次回溯到上一节点。如果所有节点的所有选择都尝试过仍然不能到达满足约束条件的终结状态,则该题无解。
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路:题目要求是在矩阵中找到包含某一字符串路径。
矩阵:
将矩阵中的元素一个一个遍历,开始a 和”bcced”中第一个元素b不一致,b
和”bcced”中第一个元素b一致,将矩阵中的b元素上下左右元素对比”bcced”的第二个,b的右边元素c符合,再讲矩阵中c元素的上下左右元素对比”bcced”的第三个…,要是都不符合则回退到上一元素检查,如果所有节点的所有选择都尝试过仍然不能到达满足约束条件的终结状态,则该题无解。
`
public class Solution {
//matrix用来存储矩阵元素
//rows 矩阵行数
//cols 矩阵列数
//str 路径字符串
public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
if(rows<=0||cols<=0||matrix.length==0||matrix==null||str.length==0||str.length>matrix.length)
return false;
//判断有没有被访问
boolean[] isVisited=new boolean[matrix.length];
int row,col;
for(int i=0;i<matrix.length;i++){
row=i/cols; //在第几行
col=i%cols; //在第几列
if(isPath(matrix,rows,cols,str,row,col,0,isVisited)){
return true;
}
}
return false;
}
public boolean isPath(char[] matrix, int rows, int cols, char[] str, int i,int j,int k,boolean[] isVisited){
//如果k==str.length说明已经包含有改路径了
if(k==str.length)
return true;
if(i<0||j<0||i>=rows||j>=cols){
return false;
}
int num=i*cols+j;
if(matrix[num]==str[k] && isVisited[num]==false){
//设置为访问过
isVisited[num]=true;
if(isPath(matrix,rows,cols,str,i-1,j,k+1,isVisited)||isPath(matrix,rows,cols,str,i+1,j,k+1,isVisited)
||isPath(matrix,rows,cols,str,i,j-1,k+1,isVisited)||isPath(matrix,rows,cols,str,i,j+1,k+1,isVisited))
return true;
}
//没有找到
//设为没有访问过
isVisited[num]=false;
return false;
}
}
`
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路差不多。
`
public class Solution {
public int movingCount(int threshold, int rows, int cols)
{
if(rows<0||cols<0){
return 0;
}
boolean [] isVisited=new boolean[rows*cols];
return movingCoreCount(threshold,rows,cols,0,0,isVisited);
}
public int movingCoreCount(int threshold,int rows,int cols,int i,int j,boolean[] isVisited){
int count=0;
if(check(threshold,rows,cols,i,j,isVisited)){
isVisited[i*cols+j]=true;
count=1+movingCoreCount(threshold,rows,cols,i-1,j,isVisited)+movingCoreCount(threshold,rows,cols,i+1,j,isVisited)
+ movingCoreCount(threshold,rows,cols,i,j-1,isVisited)+movingCoreCount(threshold,rows,cols,i,j+1,isVisited);
}
return count;
}
//检查是否满足条件
public boolean check(int threshold,int rows,int cols,int i,int j,boolean[] isVisited){
if(i>=0 && i<rows && j>=0 && j<cols && (getCount(i)+getCount(j))<=threshold && !isVisited[i*cols+j]){
return true;
}
return false;
}
//计算数位上的总和
public int getCount(int num){
int sum=0;
while(num>0){
sum+=num%10;
num=num/10;
}
return sum;
}
}
`
想到邦邦大佬以前问我的一个如果给你一个矩阵,有一个矩阵中的点是不能经过,请问一个点到另外一个点的的个数。
如图:
其实本质的思路还是一样的,需要注意下标记数组,isVisited。每次递归需要重新复制一份新的标记数组(找了半个小时的bug)。
`
import java.util.Arrays;
public class Solution {
public static void main(String[] args) {
int rows=3,cols=4,unReachRow=1,unReachCol=1,startRow=0,startCol=0,endRow=2,endCol=3;
System.out.println(countPath(rows,cols,unReachRow,unReachCol,startRow,startCol,endRow,endCol));
}
/**
*
* @param rows //矩阵行数
* @param cols //矩阵列数
* @param unReachRow //不能到达的点所在行
* @param unReachCol //不能到达的点所在列
* @param startRow //出发点所在行
* @param startCol //出发点所在列
* @param endRow //结束点所在行
* @param endCol //结束点所在列
*/
public static int countPath(int rows,int cols,int unReachRow,int unReachCol,int startRow,int startCol,int endRow,int endCol){
if(rows<=0||cols<=0||unReachRow>=rows||unReachRow<0||unReachCol>=cols||unReachCol<0
||startRow>=rows||startRow<0||startCol>=cols||startCol<0
||endRow>=rows||endRow<0||endCol>=cols||endCol<0)
return 0;
//标记是否被访问
boolean[] isVisited=new boolean[rows*cols];
return countCore(rows,cols,unReachRow,unReachCol,startRow,startCol,endRow,endCol,isVisited);
}
//currRow 现在点所在行
//currCol 现在点所在列
public static int countCore(int rows,int cols,int unReachRow,int unReachCol,int currRow,int currCol,int endRow,int endCol,boolean[] isVisited){
if(currRow<0||currRow>=rows||currCol<0||currCol>=cols||isVisited[currRow*cols+currCol]){
return 0;
}
//不能到达点的判断
if (currRow==unReachRow&&currCol==unReachCol)
return 0;
//到达最后终点判断
if(currRow==endRow&&currCol==endCol&&!isVisited[currRow*cols+currCol]){
isVisited[currRow*cols+currCol]=true;
return 1;
}
isVisited[currRow*cols+currCol]=true;
int count=0;
count =countCore(rows,cols,unReachRow,unReachCol,currRow-1,currCol,endRow,endCol, Arrays.copyOf(isVisited,isVisited.length))+countCore(rows,cols,unReachRow,unReachCol,currRow+1,currCol,endRow,endCol,Arrays.copyOf(isVisited,isVisited.length))+
countCore(rows,cols,unReachRow,unReachCol,currRow,currCol-1,endRow,endCol,Arrays.copyOf(isVisited,isVisited.length))+countCore(rows,cols,unReachRow,unReachCol,currRow,currCol+1,endRow,endCol,Arrays.copyOf(isVisited,isVisited.length));
return count;
}
}
`