矩阵中的路径
判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向上下左右移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
- 回溯
- 目前的理解是,回溯就是带记录的递归。如果说分治注重每个小问题的过程,那么回溯就是注重每一次递归的过程。
- 一维数组转矩阵(二维数组),方便处理。
- 一个Boolean类型的二维数组作为记录表。
- 问题转变为回溯的过程。
- 因为逻辑上还是递归,那么问题转变为跳出递归的逻辑。
a. 当查询字符串的长度,和统计时字符串的长度相同时,说明找到路径,返回true。
b. 下表越界,返回false。
c. 当本次查询的值与查询字符串不同,返回false。
d. 本次查询的值已经查询过了,返回false。(如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。) - 接下来处理带记录的递归过程。
a. 不管怎样,当前查询到的这个数,确实被查过了,所以先赋值为true。
b. 如果接下来也一直查得到,即返回了true。
c. 如果接下来某一次没查到,那这一整条链,需要重新赋值为false,在后续继续查找。
public class Solution {
private final static int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
private int rows;
private int cols;
public boolean hasPath (String val, int rows, int cols, String path) {
if (rows == 0 || cols == 0) return false;
this.rows = rows;
this.cols = cols;
char[] array = val.toCharArray();
char[][] matrix = buildMatrix(array);
char[] pathList = path.toCharArray();
boolean[][] marked = new boolean[rows][cols];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
if (backtracking(matrix, pathList, marked, 0, i, j))
return true;
return false;
}
private boolean backtracking(char[][] matrix, char[] pathList,
boolean[][] marked, int pathLen, int r, int c) {
if (pathLen == pathList.length) return true;
if (r < 0 || r >= rows || c < 0 || c >= cols
|| matrix[r][c] != pathList[pathLen] || marked[r][c]) {
return false;
}
marked[r][c] = true;
for (int[] n : next)
if (backtracking(matrix, pathList, marked, pathLen + 1, r + n[0], c + n[1]))
return true;
marked[r][c] = false;
return false;
}
private char[][] buildMatrix(char[] array) {
char[][] matrix = new char[rows][cols];
for (int r = 0, idx = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
matrix[r][c] = array[idx++];
return matrix;
}
public static void main(String[] args) {
Solution solution = new Solution();
String val = "ABCESFCSADEE";
int rows = 3;
int cols = 4;
String path = "ABCCED";
boolean res = solution.hasPath(val, rows, cols, path);
System.out.println(res);
}
}
https://github.com/CyC2018/CS-Notes/blob/master/notes/12.%20%E7%9F%A9%E9%98%B5%E4%B8%AD%E7%9A%84%E8%B7%AF%E5%BE%84.md
机器人的运动范围
地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。
例如,当 k 为 18 时,机器人能够进入方格 (35,37),因为 3+5+3+7=18。但是,它不能进入方格 (35,38),因为 3+5+3+8=19。请问该机器人能够达到多少个格子?
- 回溯
- 首先题目会设置给予我们矩阵的大小,实际上每一个框里能放的数的大小也就能确定了。这个就是递归的比较条件之一。
- 标记的方式和上一题差不多,不过上一题会有一个链路查询失败后,全部重置为false,这题就不需要了,符合标记true,不符合不变,即为false。
private static final int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
private int cnt = 0;
private int rows;
private int cols;
private int threshold;
private int[][] digitSum;
public int movingCount(int threshold, int rows, int cols) {
this.rows = rows;
this.cols = cols;
this.threshold = threshold;
initDigitSum();
boolean[][] marked = new boolean[rows][cols];
dfs(marked, 0, 0);
return cnt;
}
private void dfs(boolean[][] marked, int r, int c) {
if (r < 0 || r >= rows || c < 0 || c >= cols || marked[r][c])
return;
marked[r][c] = true;
if (this.digitSum[r][c] > this.threshold)
return;
cnt++;
for (int[] n : next)
dfs(marked, r + n[0], c + n[1]);
}
private void initDigitSum() {
int[] digitSumOne = new int[Math.max(rows, cols)];
for (int i = 0; i < digitSumOne.length; i++) {
int n = i;
while (n > 0) {
digitSumOne[i] += n % 10;
n /= 10;
}
}
this.digitSum = new int[rows][cols];
for (int i = 0; i < this.rows; i++)
for (int j = 0; j < this.cols; j++)
this.digitSum[i][j] = digitSumOne[i] + digitSumOne[j];
}
https://github.com/CyC2018/CS-Notes/blob/master/notes/13.%20%E6%9C%BA%E5%99%A8%E4%BA%BA%E7%9A%84%E8%BF%90%E5%8A%A8%E8%8C%83%E5%9B%B4.md
字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。
- 回溯
- 和之前几题有相同的地方,都是标记递归。同时带有重置的思维,重置的意思是某个参数是否被使用过,或者某个参数能否被接下来使用。
- 首先,对原始数据进行排序,保证遍历的过程中是有序的。
- 其次,遍历这个原始数据,每遍历一个字符,就将所有以这个字符开头的可能字符串存储到答案中。
- 跳出递归条件
a. 最后得到的字符和给予的字符长度相同,说明是答案。
b. 不是第一个字符,且这个字符与上一个字符一样,同时被标记为使用过,不让其重复。 - 若可以递归,则标记当前的字符为true,添加该字符到结果字符串中,进入下一轮递归。
- 在递归结束后,记得将标记回溯。
private ArrayList ret = new ArrayList<>();
public ArrayList Permutation(String str) {
if (str.length() == 0)
return ret;
char[] chars = str.toCharArray();
Arrays.sort(chars);
backtracking(chars, new boolean[chars.length], new StringBuilder());
return ret;
}
private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) {
if (s.length() == chars.length) {
ret.add(s.toString());
return;
}
for (int i = 0; i < chars.length; i++) {
if (hasUsed[i])
continue;
if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]) /* 保证不重复 */
continue;
hasUsed[i] = true;
s.append(chars[i]);
backtracking(chars, hasUsed, s);
s.deleteCharAt(s.length() - 1);
hasUsed[i] = false;
}
}
https://github.com/CyC2018/CS-Notes/blob/master/notes/38.%20%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%8E%92%E5%88%97.md
引用仓库:https://github.com/CyC2018/CS-Notes/blob/master/notes/%E5%89%91%E6%8C%87%20Offer%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md