【LeetCode剑指offer 02】矩阵中的路径(老鼠走迷宫plus,应用深度优先搜索与回溯机制)
矩阵中的路径
https://leetcode.cn/problems/ju-zhen-zhong-de-lu-jing-lcof/
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。

示例 1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
示例 2:
输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false
提示:
m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board 和 word 仅由大小写英文字母组成
思路
这题其实就是经典的算法题“老鼠走迷宫”的变体,在老鼠走迷宫中,我们要避开障碍,找到通往出口的路径
这里也一样,只不过"障碍"变成了非目标字符,"通往出口的路径"就是我们的目标字符串
参考走迷宫的解法,我们要逐层遍历矩阵(迷宫),同时判断当前遍历值的下一步遍历位置是否满足要求,不满足要求就要换方向走,直到找到满足要求的方向再继续遍历(前进)

借一下k神的图,上面描述的过程即如图中所示
一般来说,知道老鼠走迷宫的,想出本题的解题思路应该不难,但是代码实现就有点难写
代码
实现上,根据分析,肯定是需要使用回溯机制的,也就是要用递归去实现(本题就是经典的深度优先搜索题)
老规矩,按照三部曲去分析一下
代码分析
1、确定递归函数参数与返回值
因为需要对不同前进方向的状态进行确认,因此递归函数需要返回值,且类型为布尔类型
需要的参数有:待搜索的矩阵board、目标字符串word、前进方向i、j,以及当前目标字符在word中的索引k
注意点:
1、前进方向i、j是需要一个约束边界的,因此还需定义两个变量来保存矩阵的行列大小
2、这里我们需要引入一个变量k来标记当前遍历值(如果是目标字符串中的字符)位于目标字符串中的下标,这样才可以避免出现重复使用某个值的情况
class Solution {
private:
//定义变量保存数组的行列
int rows, cols;
//确定递归函数的参数与返回值
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k){
}
public:
bool exist(vector<vector<char>>& board, string word) {
}
};
2、确定终止条件
终止条件有两类:非法情况和搜索完成情况
非法情况
即遍历过程中出现的各种非法情况,以及当前遍历值不是目标字符的情况
例如当前遍历值不是要检查上下左右四个方向哪边能继续走嘛,万一某个方向超出矩阵大小范围,这就属于一类触发递归终止的条件
如果遍历值都没出现在目标字符串word中,就直接不用往该方向走了
搜索完成
k用于指示当前找到了目标字符串中的哪个字符,如果k指向word的最后一个字符,那么意味着搜索结束,返回true
class Solution {
private:
//定义变量保存数组的行列
int rows, cols;
//确定递归函数的参数与返回值
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k){
//首先确定返回false的情况
//i(行)、j(列)越界、当前矩阵遍历值不是目标字符串中的字符
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
//然后确定返回true的情况
//k用于记录当前为目标字符的遍历值在目标字符串中的位置,当k以及在目标字符串末尾,就说明所有字符均已找到
if(k == word.size() - 1) return true;
}
public:
bool exist(vector<vector<char>>& board, string word) {
}
};
3、处理单层逻辑
在当前递归层中,我们需要先将当前遍历值填充为'0'(其实就是初始化一下矩阵,因为还不知道这个新的位置是否满足条件)
然后调用递归函数dfs去当前遍历值的四个方向(上下左右)去判断哪个方向可以走下一步,直到找到可以走的方向,并把当前遍历值位置的值改为对应目标字符串中k位置的字符,然后重复上述流程,判断下一个遍历位置是否满足条件
class Solution {
private:
//定义变量保存数组的行列
int rows, cols;
//确定递归函数的参数与返回值
//递归函数中,需要对不同前进方向的状态进行确认,因此返回值是布尔类型
//需要的参数有:待搜索的矩阵board、目标字符串word、前进方向i、j,以及当前目标字符在word中的索引k
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k){
//首先确定返回false的情况
//i(行)、j(列)越界、当前矩阵遍历值不是目标字符串中的字符
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
//然后确定返回true的情况
//k用于记录当前为目标字符的遍历值在目标字符串中的位置,当k以及在目标字符串末尾,就说明所有字符均已找到
if(k == word.size() - 1) return true;
//先当前遍历值统一填充为0(因为还不知道这个新的位置是否满足条件)
board[i][j] = '0';
//递归调用dfs对当前遍历值的下一个位置进行判断(上下左右)
bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1)
|| dfs(board, word, i, j - 1, k + 1) || dfs(board, word, i, j + 1, k + 1);
//如果四个方向中有一个走得通(即返回true),那就把矩阵中当前位置的字符修改为目标字符串中k指向的相应字符
//该操作是在最深的一层递归中完成的
board[i][j] = word[k];
return res;//向上一层递归返回本层递归结果
}
public:
bool exist(vector<vector<char>>& board, string word) {
}
};
完整代码
主函数中,需要获取矩阵的行列(别搞混)
然后使用两层for循环遍历矩阵,调用递归判断结果,如果递归函数返回的是true,那么就结束
如果for遍历结束都没返回,那就返回false
class Solution {
private:
//定义变量保存数组的行列
int rows, cols;
//确定递归函数的参数与返回值
//递归函数中,需要对不同前进方向的状态进行确认,因此返回值是布尔类型
//需要的参数有:待搜索的矩阵board、目标字符串word、前进方向i、j,以及当前目标字符在word中的索引k
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k){
//首先确定返回false的情况
//i(行)、j(列)越界、当前矩阵遍历值不是目标字符串中的字符
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
//然后确定返回true的情况
//k用于记录当前为目标字符的遍历值在目标字符串中的位置,当k以及在目标字符串末尾,就说明所有字符均已找到
if(k == word.size() - 1) return true;
//先当前遍历值统一填充为0(因为还不知道这个新的位置是否满足条件)
board[i][j] = '0';
//递归调用dfs对当前遍历值的下一个位置进行判断(上下左右)
bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1)
|| dfs(board, word, i, j - 1, k + 1) || dfs(board, word, i, j + 1, k + 1);
//如果四个方向中有一个走得通(即返回true),那就把矩阵中当前位置的字符修改为目标字符串中k指向的相应字符
//该操作是在最深的一层递归中完成的
board[i][j] = word[k];
return res;//向上一层递归返回本层递归结果
}
public:
bool exist(vector<vector<char>>& board, string word) {
//获取矩阵的行列长度
rows = board.size();
cols = board[0].size();
//遍历矩阵
for(int i = 0; i < rows; ++i){
for(int j = 0; j < cols; ++j){
if(dfs(board, word, i, j, 0)) return true;//调用dfs判断目标字符串在矩阵中是否存在,k初始值为0
}
}
return false;
}
};
【LeetCode剑指offer 02】矩阵中的路径(老鼠走迷宫plus,应用深度优先搜索与回溯机制)的更多相关文章
- 剑指 Offer 12. 矩阵中的路径 + 递归 + 深搜 + 字符串问题
剑指 Offer 12. 矩阵中的路径 题目链接 题目类似于迷宫的搜索. 需要注意的是,需要首先判断起始搜索的位置,可能有多个起点,都需要一一尝试. 每轮迭代的时候记得将是否遍历标记数组还原为未遍历的 ...
- 力扣 - 剑指 Offer 12. 矩阵中的路径
题目 剑指 Offer 12. 矩阵中的路径 思路1(回溯.DFS) 这题可以使用回溯+递归来解决,思路如下: 将二维数组的每一个元素都作为起点进行回溯查找 每次查找的时候,都有四个方向,但是上一个方 ...
- 【Java】 剑指offer(11) 矩阵中的路径
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字 ...
- Go语言实现:【剑指offer】矩阵中的路径
该题目来源于牛客网<剑指offer>专题. 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向 ...
- 用 Go 剑指 Offer 12. 矩阵中的路径
给定一个 m x n 二维字符网格 board 和一个字符串单词 word .如果 word 存在于网格中,返回 true :否则,返回 false . 单词必须按照字母顺序,通过相邻的单元格内的字母 ...
- 剑指Offer 65. 矩阵中的路径 (回溯)
题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中 ...
- [剑指Offer] 65.矩阵中的路径
题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中 ...
- 剑指offer:矩阵中的路径(递归回溯法DFS类似迷宫)
1. 题目描述 /* 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径. 路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子. 如果一条 ...
- 剑指offer——13矩阵中的路径
题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中 ...
- 剑指 Offer 12. 矩阵中的路径
题目描述 是一道很常见的深搜题目,不过里面要考虑一些边界问题,比如走过的路径是不能再次走入的,所以我这里我自己的 代码想到是利用一个新的二维的数组,记录走过的路径,不过题解的直接将原二维数组中的路径隐 ...
随机推荐
- [转帖]超能课堂(323) 为什么WiFi实际速率只有标称速率的一半?
超能课堂(323) 为什么WiFi实际速率只有标称速率的一半? 开始的地方 协议速率与实际速率有何不同? 什么是"全双工"与"半双工"? 无线网络与有线网络的抗 ...
- [转帖]如何理解 iowait
Linux中,%iowait 过高可能是个问题,严重的时候,它能使服务停止, 但问题是,多高才算高? 什么时候应该担心呢? 本文将讨论 iowait 的含义.相关的统计数据.原理以及 iowait的瓶 ...
- [转帖]SHELL—— awk两个特殊模式(BEGIN 和 END)及awk高级应用(条件判断、循环)
一.Awk 的两个特殊模式 BEGIN 和 END,BEGIN 被放置在没有读取任何数据之前,而 END 被放置在所有的数据读取完成以后执行 体现如下: BEGIN{}: 读入第一行文本之前执行的语句 ...
- Promise.all()方方详解
1.Promise.all()方方详解 Promise.all,只有所有的Promise成功,才能够算作成功,只要有一个失败了,就直接失败: 它包含一个参数,这个参数是指包含多个Promise的数组: ...
- flask session 伪造
flask session 伪造 一.session的作用 由于http协议是一个无状态的协议,也就是说同一个用户第一次请求和第二次请求是完全没有关系的,但是现在的网站基本上有登录使用的功能,这就要求 ...
- Linux挂载新磁盘到根目录
1.添加磁盘到需要挂载的机器上2.lsblk查看硬盘挂载情况,sdb,sdc为我新挂载的磁盘 3.fdisk -l查看挂载之前的分区情况, 4.为新硬盘创建分区 fdisk /dev/sdb,终端会提 ...
- 希尔伯特变换用于解调系统——以解调调频信号为例,FM Demodulation
What's The Hilbert Transform 简单地说,希尔伯特变换的物理意义为:把信号的所有频率分量的相位推迟90度,这样原信号和变换后信号可以视为一组IQ正交信号,在数字域正交化,可以 ...
- 每日一道面试题:Java中序列化与反序列化
写在开头 哈喽大家好,在高铁上码字的感觉是真不爽啊,小桌板又拥挤,旁边的小朋友也比较的吵闹,影响思绪,但这丝毫不影响咱学习的劲头!哈哈哈,在这喧哗的车厢中,思考着这样的一个问题,Java中的对象是如何 ...
- 小型命令解析器|minShell|多进程|重定向|进程控制【超详细的代码注释和解释】
前言 那么这里博主先安利一下一些干货满满的专栏啦! 手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014. ...
- ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR详解
堆栈的实现方法 在随机存储器区划出一块区域作为堆栈区,数据可以一个个顺序地存入(压入)到这个区域之中,这个过程称为'压栈'(push ).通常用一个指针(堆栈指针 SP-StackPointer ...