剑指Offer:回溯方法解题,寻找路径问题(一)

题目描述:

      请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 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;
	}

猜你喜欢

转载自blog.csdn.net/Raven_csdn/article/details/81115909