矩阵中的路径

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,应用深度优先搜索与回溯机制)的更多相关文章

  1. 剑指 Offer 12. 矩阵中的路径 + 递归 + 深搜 + 字符串问题

    剑指 Offer 12. 矩阵中的路径 题目链接 题目类似于迷宫的搜索. 需要注意的是,需要首先判断起始搜索的位置,可能有多个起点,都需要一一尝试. 每轮迭代的时候记得将是否遍历标记数组还原为未遍历的 ...

  2. 力扣 - 剑指 Offer 12. 矩阵中的路径

    题目 剑指 Offer 12. 矩阵中的路径 思路1(回溯.DFS) 这题可以使用回溯+递归来解决,思路如下: 将二维数组的每一个元素都作为起点进行回溯查找 每次查找的时候,都有四个方向,但是上一个方 ...

  3. 【Java】 剑指offer(11) 矩阵中的路径

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字 ...

  4. Go语言实现:【剑指offer】矩阵中的路径

    该题目来源于牛客网<剑指offer>专题. 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向 ...

  5. 用 Go 剑指 Offer 12. 矩阵中的路径

    给定一个 m x n 二维字符网格 board 和一个字符串单词 word .如果 word 存在于网格中,返回 true :否则,返回 false . 单词必须按照字母顺序,通过相邻的单元格内的字母 ...

  6. 剑指Offer 65. 矩阵中的路径 (回溯)

    题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中 ...

  7. [剑指Offer] 65.矩阵中的路径

    题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中 ...

  8. 剑指offer:矩阵中的路径(递归回溯法DFS类似迷宫)

    1. 题目描述 /* 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径. 路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子. 如果一条 ...

  9. 剑指offer——13矩阵中的路径

    题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中 ...

  10. 剑指 Offer 12. 矩阵中的路径

    题目描述 是一道很常见的深搜题目,不过里面要考虑一些边界问题,比如走过的路径是不能再次走入的,所以我这里我自己的 代码想到是利用一个新的二维的数组,记录走过的路径,不过题解的直接将原二维数组中的路径隐 ...

随机推荐

  1. 二进制安装Mysql数据库的快速方法

    二进制安装Mysql数据库的快速方法 摘要 还是国产操作系统 rpm包可能不太兼容,为了简单准备使用tar包方式安装mysql数据库 这里简单记录一下过程. 为以后使用. 介质下载 下载二进制的tar ...

  2. 简单定位占用最高CPU的java进程信息

    公司里面一个应用不小心点击就会导致系统性能下降很明显. 性能组的同事定位到了, 我这里以学习的态度重现一下这个过程. 1. 问题再现 产品一个非常大数据量的帮助, 点击之后就会占用非常多的cpu 因为 ...

  3. SHA加密在实际应用中的优势与局限

    SHA加密算法简介 SHA(Secure Hash Algorithm)加密算法是一种单向加密算法,常用于加密数据的完整性校验和加密签名.它是由美国国家安全局(NSA)设计并广泛应用于各种安全场景.S ...

  4. Vue中is属性的用法 可以动态切换组件

    is 是组件的一个属性,用来展示组件的名称 is和component联用哈 vue提供了component来展示对应的组件名称 compont是一个占位符,is这个属性,用来展示对应的组件名称 三个子 ...

  5. axios发送请求时携带token

    请求头携带token async getUserlist(){ // 需要授权的Api,必须在青丘头中使用Authorization 字段提供token令牌 const AUTH_TOKEN=loca ...

  6. 设计模式学习-使用go实现状态模式

    状态模式 定义 优点 缺点 适用范围 代码实现 参考 状态模式 定义 状态模式(state):当一个条件的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对 ...

  7. LeetCode刷题日记 2020/08/18

    给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: [-10 ...

  8. 苹果iOS 17.2年底推送:iPhone 15 Pro的自定义操作按钮功能升级

    据报道,苹果会在年底推送iOS 17.2版本,新版系统将会修复iPhone 15系列WiFi速度慢的问题. 与此同时,iOS 17.2将会带来翻译功能,iPhone 15 Pro的自定义操作按钮切换到 ...

  9. 【C语言深度解剖】一篇搞懂结构体内存对齐【结构体的大小你会算了吗】

    结构体内存对齐 大家好,我是西城s 文章目录 前言 引入 结构体内存对齐 `offsetof`宏的使用 为什么存在结构体内存对齐 修改默认对齐数 尾声 前言 作者: #西城s 这是我的主页:#西城s ...

  10. 离线解锁 CodeCombat 全关卡教程 使用docker安装实现

    前期准备 下载安装docker desktop https://www.123pan.com/s/fmvUVv-HqApH, 这个安装不会的随便搜一个教程,挺多的.我随便找了一个知乎的 Windows ...