题目描述:
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
这个问题刚开始看到的时候,那时候还不知道什么时候回溯法,但我知道必须暴力的尝试,比如从(0,0)开始,分别和路径的第一个、第二个、第三个比较,能完全比配成功则为true,否则为false。我也想到了给每一个元素做标记,标记它是否被访问过,因为题目要求不能重复,我想的用Java中的Map<Integer,Boolean> 一一做对应,但是不会编程。后来才知道,回溯法一般用递归编程。
直接上代码,加注释:
public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
//程序的健壮性,当数组为空的时候,路径字符串为空的时候,或者行和列的个数为0的时候,都为false
if (matrix == null || str == null || rows <= 0 || cols <= 0) {
return false;
}
//申请 一个和矩阵大小一样的boolean型数组,来标记每个元素是否是路径。
boolean[] flags = new boolean[rows * cols];
//这句是用false快速填充标记数组,这样写代码比较简洁,自己写也麻烦。
//Arrays.fill(boolean[] a,boolean val)的源码也非常简单如下:
/*public static void fill(boolean[] a, boolean val) {
for (int i = 0, len = a.length; i < len; i++)
a[i] = val;
}*/
Arrays.fill(flags, false);
//从每一个起点开始尝试,直到最后一个元素也没有找到合适的路径,就结束返回false.
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
//如果有一个路径找到了,则返回为true.
if (newVisited(matrix, str, flags, i, j, rows, cols, 0)) {
return true;
}
}
}
return false;
}
/***
* 这是递归方法
* @param chs 输入字符数组
* @param str 要判断的路径
* @param flags 每个字符是否访问过的标记
* @param row 当前判断的行
* @param col 当前判断的列
* @param rows 有多少行
* @param cols 有多少列
* @param count 判断到路径的第几个字符
* @return 返回是否有该路径
*/
public boolean newVisited(char[] chs, char[] str, boolean[] flags, int row, int col, int rows, int cols,
int count) {
//递归的真值结束条件,匹配路径的所有字符,返回真值。
if (count >= str.length) {
return true;
}
//这是一个标记,标记如果在该位置匹配,那么它的下一个位置(上、下、左、右移动),是否匹配,如果不匹配,
//该位置也是无效
boolean flag = false;
//要判断的行和列是否大于等于0,是否小于行和列,是否别访问过,是否和要匹配的字符相等。
//如果都满足,判断下一个字符
if (row >= 0 && row < rows && col >= 0 && col < cols && !flags[cols * row + col]
&& chs[cols * row + col] == str[count]) {
//这就是判断下一个字符
count++;
//把刚才访问过的字符,做一个被访问过的标记
flags[cols * row + col] = true;
//当前字符匹配,看看它的4个方向上,是否都匹配下一个字符呢?
flag = newVisited(chs, str, flags, row, col - 1, rows, cols, count)
|| newVisited(chs, str, flags, row, col + 1, rows, cols, count)
|| newVisited(chs, str, flags, row - 1, col, rows, cols, count)
|| newVisited(chs, str, flags, row + 1, col, rows, cols, count);
//完了四个方向都不匹配
if (!flag) {
//好吧,就当我没有访问过你,毕竟从你这里没法走下一步呀(匹配下一个字符),标记置成未访问。
flags[cols * row + col] = false;
//下一个字符不匹配,回到原来的字符吧,看看有其它的路不。
count--;
}
}
return flag;
}