題目描述:
請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則之后不能再次進入這個格子。 例如 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;
}
}