【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. 矩阵中的路径
题目描述 是一道很常见的深搜题目,不过里面要考虑一些边界问题,比如走过的路径是不能再次走入的,所以我这里我自己的 代码想到是利用一个新的二维的数组,记录走过的路径,不过题解的直接将原二维数组中的路径隐 ...
随机推荐
- [转帖]linux时间戳转换成时间指令_时间戳转换公式
原文地址:http://wanping.blogbus.com/logs/28663569.html 1.时间戳转换为正常显示的时间格式 Freebsd 系统下: 转换命令为: date -r 111 ...
- [转帖]QPS、TPS、RT、并发数、吞吐量理解和性能优化深入思考
https://baijiahao.baidu.com/s?id=1675704570461446033&wfr=spider&for=pc 吞吐量 在了解qps.tps.rt.并发数 ...
- SQLSERVER 数据库根据LCK_M_S对应的waitsorce 查看被锁的表信息的简单方法
公司一个开发大牛召冠总搞过一个 DMSQLMONITOR 工具 能够识别Oracle以及SQLSERVER 数据库的锁和事务等问题, 非常好用 今天环境出现了不可用的情况, 所以这边着急进行一下问题分 ...
- Registration Authority 简介
RA 功能简介 在公共密钥基础设施(PKI)中,CA(Certificate Authority,证书颁发机构)系统的RA(Registration Authority,注册机构)是PKI体系结构的重 ...
- TienChin 活动管理-设置活动的默认状态
// 设置活动未过期,相当于新增的活动,默认都是未过期的 activity.setActivityStatus(1);
- springboot security 权限控制 -- @PreAuthorize 的使用
1. 说明 security 鉴权方式常用的有两种配置,1.配置文件中配置:2.使用注解标注:他们都是基于 acess 表达式,如果需要自定义逻辑的鉴权认证,只需要自定义 access 表达式即可.本 ...
- linxu下面的绝对路径和相对路径
绝对路径和相对路径 前言 相对路径与绝对路径 绝对路径 相对路径 目录的相关操作 绝对路径和相对路径 前言 学习linux,对于里面的路径肯定要很清楚.做下总结吧. 相对路径与绝对路径 绝对路径 路径 ...
- Linux 中通过虚拟地址获取物理地址并锁定
在 Linux 开发过程中,申请内存后,某些时候需要用物理地址传给其他外设进行写入或者读取操作,同时考虑到防止被操作系统 sawp,导致实际的物理地址发生变化,从而在操作对应的虚拟地址时无法正常运行等 ...
- AI热点概念解读:一文搞懂这些热词
自 ChatGPT 问世以来,AI的风口就来了. AI是一门研究如何使计算机具有类似人类智能的学科. 自从ChatGPT-3.5给大家带来了极大的震惊之后,全民都在谈论AI,在这个AI大时代背景之下, ...
- ASP.NET Core分布式项目实战(课程介绍,MVP,瀑布与敏捷)--学习笔记
任务1:课程介绍 课程目标: 1.进一步理解 ASP.NET Core 授权认证框架.MVC 管道 2.掌握 Oauth2,结合 Identity Sercer4 实现 OAuth2 和 OpenID ...