矩阵中的路径
题目链接:
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 [ a b c e s f c s a d e e ] \begin{bmatrix} a & b & c &e \\ s & f & c & s \\ a & d & e & e\end{bmatrix} ⎣⎡asabfdcceese⎦⎤ 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路
首先在矩阵中任选一个格子作为路径的起点,假设矩阵中某个格子的字符为ch,并且这个格子将对应于路径上的第i个字符,如果路径上第i字符不是ch,那么这个格子不可能处于路径上的第i个位置,如果路径上的第i个位置正好ch,那么到相邻的格子寻找路径上的第i+1个字符,除矩阵边界上的格子之外,其他格子都有4个相邻的格子,重复这个过程,直到路径上的所有字符都在矩阵中找到相应的位置
使用回溯法来解决这个问题,路径可以被看成一个栈,当在矩阵中定位了路径上的前n个字符的位置后,在与第n个字符对应的格子周围都没有找到n+1个字符,则返回路径上的n-1个字符,重新定位第n个字符,回溯的实现使用递归来实现同时还需要创建一个和矩阵一样大小的boolean类型的矩阵用来标记是否已经访问过这个位置
实现
public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
// 创建访问过的路径标记
boolean[] viested = new boolean[matrix.length];
// 寻找匹配的开始
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
if(dfs(matrix, row, rows, col, cols, str, 0, viested)) return true;
}
}
return false;
}
// 继续寻找匹配的字符串
public boolean dfs(char[] matrix, int row, int rows, int col, int cols, char[] str, int len, boolean[] viested) {
// bad case 返回
if (row < 0 || row >= rows || col < 0 || col >= cols || str[len] != matrix[row * cols + col] || viested[row * cols + col])
return false;
// 及时返回 到最后了就证明OK了 返回 找了最后一位
if (len == str.length -1 ) return true;
// 设置已经访问过了
viested[row * cols + col] = true;
// 递归访问一个节点的四周的四个节点
boolean isExit = dfs(matrix, row + 1, rows, col, cols, str, len + 1, viested) ||
dfs(matrix, row - 1, rows, col, cols, str, len + 1, viested) ||
dfs(matrix, row, rows, col + 1, cols, str, len + 1, viested) ||
dfs(matrix, row, rows, col - 1, cols, str, len + 1, viested);
// 遍历结束后恢复已经访问的标记
viested[row * cols + col] = false;
return isExit;
}
机器人运动轨迹
题目描述:
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路
这个题和上一个题目很类似也可以使用回溯的方法来解决,但是这两个题目有略微的不同,不同点在于上一个题目是在二维数组中查找路径,没有规定起点,但是这道题目规定了开始的位置,同时两个题目的终止条件不同,上一个题目是查找是否存在路径,这个题目是求用几个满足条件的解。
从矩阵的(0,0) 开始遍历二维矩阵,同时创建一个boolean数组标记是否已经访问过了相应的位置,开始递归的遍历这个不许要查找的时候后头去找,于是不需要想上一个题目一样返回去更改boolean数组,编写计算位数之和的函数判断是否超过了给定的K值,于是代码实现如下和上一面的一题类似但是有些许的不同。
实现
public int movingCount(int threshold, int rows, int cols) {
boolean[] visited = new boolean[rows * cols];
return dfs(threshold, 0, rows, 0, cols, visited);
}
public int dfs(int threshold, int row, int rows, int col, int cols, boolean[] visited) {
if (row < 0 || row >= rows || col < 0 || col >= cols || visited[row * cols + col] || cal(row, col) > threshold) return 0;
visited[row * cols + col] = true;
return dfs(threshold, row + 1, rows, col, cols, visited) +
dfs(threshold, row - 1, rows, col, cols, visited) +
dfs(threshold, row, rows, col + 1, cols, visited) +
dfs(threshold, row, rows, col - 1, cols, visited) + 1;
}
public int cal(int row, int col) {
int rowSum = 0;
int colSum = 0;
while (row > 0) {
rowSum += row % 10;
row = row/10;
}
while (col > 0) {
colSum += col % 10;
col /= 10;
}
return rowSum + colSum;
}
总结
对面题目为二维数组的题目可以思考下能否用相似的回溯方法解决,通常回溯法适合用递归的代码实现,当我们到达某一个节点适合,尝试所有可能的选项并在满足条件的前提下递归的达到下一个节点。