【剑指Offer】65、矩阵中的路径
【剑指Offer】65、矩阵中的路径
题目描述:
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
解题思路:
本题实际并不复杂,是一个可以用回溯法解决的典型题目。所谓回溯法,就是有组织的进行穷举搜索的过程,通过深度优先来对所有可能的解进行穷举。
首先,遍历整个矩阵,我们可以找到和字符串str首字符相同的单元格,作为查找的起点,然后遍历它的上下左右四个字符,如果有和str下一个字符相同的,就以该单元格作为下一个遍历起点,依次进行,如果没有找到就回退到上一个字符,重新查找其他方向。整个过程是一个非常典型的回溯思路。
由于回溯法天生的递归特性,路径可以看做一个栈,当在矩阵中定位了路径中前n个字符的位置后,在第n个字符的上下左右(没有遍历过的)格子找第n+1个字符,如果没有找到,只能回退到第n-1个字符,重新定位第n个字符。
由于路径不能重复进入矩阵的格子,所以还需要一个和矩阵大小相同的布尔矩阵来标识每个单元格是否被访问过,这也是回溯法的惯用方法。
不难想到,实际上这个过程就对应了一棵树的深度优先遍历,具体可以结合以下代码进一步理解。
举例:
编程实现(Java):
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str){
//回溯法搜索
if(matrix==null||rows<1||cols<1||str==null)
return false;
char[][] mat=new char[rows][cols];
boolean[][] flags=new boolean[rows][cols]; //标记每个节点是否已经访问过
for(int i=0;i<rows;i++){ //转化为矩阵
for(int j=0;j<cols;j++){
mat[i][j]=matrix[cols*i+j];
flags[i][j]=false;
}
}
//以上是数据准备,下面开始搜索
//从每个元素依次开始搜索
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){ //以mat[i][j]开始
if(hasPath(mat,i,j,rows,cols,flags,str,0)==true) //找到了返回真
return true;
}
}
return false;
}
boolean hasPath(char[][] mat,int i,int j,int rows, int cols,boolean[][] flags,char[] str,int start){ //以mat[i][j]开始搜索
if(start==str.length) //str元素已经遍历完
return true;
if(i<0||i>=rows||j<0||j>=cols) //i,j超出范围
return false;
boolean res=false;
//i,j在范围内
if(mat[i][j]==str[start] && flags[i][j]==false) { //当前相等
flags[i][j]=true;
++start;
//上下左右四个方向查找
res=hasPath(mat,i-1,j,rows,cols,flags,str,start)||
hasPath(mat,i+1,j,rows,cols,flags,str,start) ||
hasPath(mat,i,j-1,rows,cols,flags,str,start) ||
hasPath(mat,i,j+1,rows,cols,flags,str,start);
if(res==false){ //没有找到,回溯
--start;
flags[i][j]=false;
}
}
return res;
}
}