剑指 Offer 12. 矩阵中的路径

最近做了一个剑指offer上的 剑指 Offer 12. 矩阵中的路径
这道题用了dfs和回溯算法,挺有典型意义的,在这里,列举一个,分析分析,加强记忆。

题目
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[["a","b","c","e"],
["s","f","c","s"],
["a","d","e","e"]]

但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

 

示例 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
提示:

1 <= board.length <= 200
1 <= board[i].length <= 200

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析

这道题就是要求先在数组中找到目标字符串的第一个字符及word[0],然后在这个元素的上下左右四个方向上中下一个字符,为了保证每个元素只能遍历一遍,可以将遍历过得元素变为true,但如果一条路真的无解了,就回溯到上一个元素上,这就需要一个变量来记录上一个元素。

分析题意, 这道题需要求目标字符串的路径, 且只要找到一个满足条件的即可, 所以特别适合用 DFS 来做. 因为 DFS 比较方便记录每条遍历过的路径, 正好可以用在这里

所以我们可以从任意一个字符开始, 依次比较它和目标字符串当前下标对应的字符, 如果相等的话, 就可以继续向四周遍历, 同时字符串下标加 1

直到字符串下标达到字符串长度, 这个时候就说明我们找到了一个满足要求的路径, 直接返回 true 即可。
.
而如果遍历过程中某个字符并不匹配, 我们就不要继续往下遍历下去了, 因为肯定不满足条件

外注意需要判断某个字符是否已经访问过, 所以我们需要额外定义一个 visit 集合, 用于存储已经访问的元素行列下标: 在递归调用某个字符之前将其加入行列下标加入集合中, 调用结束后还要将其移除, 恢复现场, 避免这个下标之后在其他路径中不能被用到
实现
经典的 DFS, 需要额外 visit 集合, 以及当前目标字符串的下标
依次遍历矩阵的每个字符, 如果当前字符与目标字符串的开头相同, 就以它为起点进行 DFS, 如果发现一条满足条件的路径就直接返回 true, 否则继续遍历
优化
visit 集合可以利用对原始矩阵的字符替换为 None 或其他不存在的字符代替, 而恢复时再将其替换回来即可, 这样可以节省一些空间

如果某个方向已经找到路径了, 就没必要继续遍历下去, 直接返回即可, 这样剪枝可以节省一些时间

代码
/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
var exist = function(board, word) {
//dfs算法和回溯算法
    for(let i = 0;i<board.length;i++){
        for(let j = 0;j<board[0].length;j++){
            if(board[i][j]===word[0]){
                let isflag = dfs(board,word,i,j,0)
                if(isflag)return true  //isflag是true,就说明找到了
            }  
        }
    }
    return false
};
//board是原数组,Word是目标数组,i是行,j是列,vis是目标数组的第几个数
   function dfs(board,word,i,j,vis){
 //中止条件
    if(i<0||j<0 || i>=board.length || j>=board[0].length){
        return false;
    }
    //获取Word目标的第VIS个字符,初始下标为0
    let curChar = word.charAt(vis);
    //如果字符不同则返回
    if(curChar !== board[i][j]){
        return false;
    }
    //如果单词最后一个字符匹配成功,则返回true
    if(vis === word.length - 1){
        return true;
    }
    //如果当前字符相同,则继续向下搜索
    let oldChar = board[i][j];
    //遍历过得数组设置为true
    board[i][j] = true;
    //四个方位遍历找到目标数
       let flag = dfs(board,word,i+1,j,vis+1)||
                  dfs(board,word,i-1,j,vis+1)||
                  dfs(board,word,i,j+1,vis+1)||
                  dfs(board,word,i,j-1,vis+1);
    // flag是false,代表这条路行不通,所以回溯到上一层的元素上去
    board[i][j] = oldChar
    return flag
    }

代码的再优化


/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
var exist = function(board, word) {
     for(let i = 0;i<board.length;i++)
        for(let j = 0;j<board[i].length;j++)
            if(dfs(board,word,0,i,j))
            return true
        
    return false
function dfs(board,word,u,x,y){
     if(board[x][y] !==word[u]) return false;
    if(u === word.length-1) return true;
   
    let dx = [-1,0,1,0],dy = [0,1,0,-1];
    let t = board[x][y];
    board[x][y] = '-';
    for(let i = 0;i<4;i++){
        let a = x+dx[i],b = y +dy[i];
        if(a>=0&&a<board.length &&b>=0&&b<board[a].length){
            if(dfs(board,word,u+1,a,b)) return true
        }
    }
    board[x][y] = t;
    return false
}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值