PS:《剑指offer》是很多同学找工作都会参考的一本面试指南,同时也是一本算法指南(为什么它这么受欢迎,主要应该是其提供了一个循序渐进的优化解法,这点我觉得十分友好)。现在很多互联网的算法面试题基本上可以在这里找到影子,为了以后方便参考与回顾,现将书中例题用Java实现(第二版),欢迎各位同学一起交流进步。
GitHub: https://github.com/Uplpw/SwordOffer。
剑指offer完整题目链接: https://blog.csdn.net/qq_41866626/article/details/120415258
1 题目描述
设计一个函数,用来判断一个矩阵中是否存在一条包含某字符串的路径。
(1)起点随意;(2)路径的移动只能是上下左右;(3)访问过的位置不能再访问。以下图矩阵为例,包含“bfce”,但是不包含“abfb”。
a b t g
c f c s
j d e h
leetcode链接: 矩阵中的路径(以下代码已测试,提交通过)
2 测试用例
一般是考虑功能用例,特殊(边缘)用例或者是反例,无效测试用例这三种情况。甚至可以从测试用例寻找一些规律解决问题,同时也可以让我们的程序更加完整鲁棒。
(1)功能用例:存在的字符串路径,不存在的路径(包括出现重复字符,没有相应字符)。
(2)边缘用例:矩阵只有一行或者一列,矩阵或者路径字母都是一样的。
(3)无效用例:无效矩阵或者字符串。
3 思路
分析:
根据题目描述,很适合用回溯法求解。
回溯法适合解决由多个步骤组成的问题,且每个步骤都有多个选项。当在某一步选择了其中一个选项,就进入下一步,然后面临新的选项。如果当前的选项已经确定没有正确答案,就回溯到上一步,在上一步选择另一选项,重复进行。回溯法的求解过程一般都可以用树状结构表示出来。实现代码很适合用递归完成。
至于本题,需要明确递归的约束条件,回溯的终止条件,标记矩阵的标记与清除时机。
- 约束条件,标记矩阵当前位置没有走过且匹配了字符串中的当前字符
- 终止条件:匹配的字符总数超过或者等于给定的字符串长度
- 标记矩阵的标记:初始化标记矩阵,把要走的当前位置标记true,
- 标记矩阵的清除时机:当前位置的下一步返回false,说明当前路径不对,需要清除,防止影响其他路径
4 代码
算法实现:
public class HasPathWithBacktracking {
public static boolean hasPath(char[][] board, String str) {
if (board == null || board.length == 0 || str == null || str.length() == 0) {
return false;
}
int row = board.length;
int col = board[0].length;
boolean[][] flag = new boolean[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (hasPathCore(board, i, j, flag, str, 0)) {
return true;
}
}
}
return false;
}
public static boolean hasPathCore(char[][] board, int rowIndex, int colIndex, boolean[][] flag, String str, int strIndex) {
// 递归结束条件
if (strIndex >= str.length()) {
return true;
}
// 防止下面的索引越界
if (rowIndex < 0 || colIndex < 0 || rowIndex >= board.length || colIndex >= board[0].length) {
return false;
}
// 当前节点没有走过且匹配当前字符
if (!flag[rowIndex][colIndex] && board[rowIndex][colIndex] == str.charAt(strIndex)) {
flag[rowIndex][colIndex] = true;
boolean result = hasPathCore(board, rowIndex + 1, colIndex, flag, str, strIndex + 1) ||
hasPathCore(board, rowIndex - 1, colIndex, flag, str, strIndex + 1) ||
hasPathCore(board, rowIndex, colIndex + 1, flag, str, strIndex + 1) ||
hasPathCore(board, rowIndex, colIndex - 1, flag, str, strIndex + 1);
if (result == true) {
return true;
} else {
// 重新设置flag值,防止对其他路径有影响
flag[rowIndex][colIndex] = false;
return false;
}
} else {
return false;
}
}
public static void main(String[] args) {
char[][] data = {
{'a', 'b', 't', 'g'},
{'c', 'f', 'c', 's'},
{'j', 'd', 'e', 'h'}};
System.out.println(hasPath(data, "bfce")); //true
System.out.println(hasPath(data, "abfb")); //false,访问过的位置不能再访问
}
}
参考
在解决本书例题时,参考了一些大佬的题解,比如leetcode上的官方、K神,以及其他的博客,在之后的每个例题详解后都会给出参考的思路或者代码链接,同学们都可以点进去看看!
本例题参考:
本文如有什么不足或不对的地方,欢迎大家批评指正,最后希望能和大家一起交流进步、拿到心仪的 offer !!!