【JavaScript题解】剑指 Offer 12. 矩阵中的路径

算法题解——保姆级注释


一、前言

矩阵中的路径是应用深度优先遍历算法的一道经典题目,在力扣中的难度等级是中等,这里先简单介绍一下平时经常接触到的深搜算法,这种算法在树和图的数据结构中有着非常普遍的应用。
深度优先搜索(Depth-First-Search,DFS
在这里插入图片描述
用最直观的树来展示,在上图中,目标是遍历访问所有节点,如果是广度优先搜索,会逐层优先从左往右进行遍历,遍历顺序为ABCDEFG,而如果是深度优先,则会是优先从上往下进行搜索,则遍历顺序为ABDECFG
当然,在实际的场景下,不一定是这样的遍历顺序,但是应用的是相同的遍历思想。

二、题目

那我们现在来看看题目


给定一个 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

提示:

1 <= board.length <= 200
1 <= board[i].length <= 200
board 和 word仅由大小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof


简单来说,题目给你一个字母矩阵和一个单词,看你能不能用笔把这个字母在图中连续的涂出来(是否存在),这当然需要我们遍历搜索可能的路径,并在过程中对各种情况进行判断了。

三、题解

直接上代码

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
var exist = function(board, word) {
    if(board.length*board[0].length<word.length) return false;  //若矩阵元素小于单词长度,必不存在
    let x=0;                       //单词的当前长度
    board = board.map((e)=>{       //这里对矩阵进行预处理,每个元素添加一个标记判断是否经过
        return e.map(d=>[d,false]);
    })
    for(let i = 0 ;i<board.length;i++){    //遍历矩阵,若元素和第一个字母相等则判断能否找到所有字母
        for(let j = 0 ;j<board[0].length;j++){
           if(board[i][j][0]==word[x]&&searchNode(i,j,x,word,board)) return true;
       }
    }
    return false
};

var searchNode = function(i,j,x,word,board){
    if(board[i][j][1]==false&&board[i][j][0]==word[x]){        //若元素未被经过,继续
        if(x==word.length-1) return true;                      //递归出口,若来到最后一位,则矩阵中存在该字母
        board[i][j][1] = true;                                 //经过的元素将标记取反
        if(board[i][j-1]&&searchNode(i,j-1,x+1,word,board)){   //如果该方向下个字母存在,递归 
            return true;                                       //该路径存在完整单词
        }
        if(board[i][j+1]&&searchNode(i,j+1,x+1,word,board)){
            return true;
        }
        if(board[i+1]&&searchNode(i+1,j,x+1,word,board)){
            return true;
        }
        if(board[i-1]&&searchNode(i-1,j,x+1,word,board)){
            return true;
        }
        board[i][j][1] = false;      //四个方向都没有结果,此路不通,逐步往前释放标记,回溯查找其他路径
    }
    return false; //此路不通,返回false
}

看看思路

  1. 首先确定基本思路,遍历每个节点,若该节点值等于单词首字母,则递归进行深搜判断此路是否符合要求
  2. 我们需要给每个元素做个标记来判断是否被搜索过,所以先对数组进行预处理,把每个元素变成二维数组,加上一个布尔值作为标记。
  3. 然后遍历数组,若该节点值等于单词首字母,就传入深搜函数递归判断。
  4. 在递归中要确定判断条件,如果该元素没有被搜索过,并且等于单词的当前字母,则可以继续搜索,否则该此路不通(返回false)。
  5. 然后给递归一个出口,如果已经到了单词的最后一个字母,证明这条路径符合要求,返回true结束搜索。
  6. 在对四个方向搜索前,需要先给现在的位置做上标记“到此一游”,将前面的布尔值取反,之后遍历到此处即可放弃该路径。
  7. 对四个方向进行搜索,通过该元素是否存在来判断是否到达边界。如果该方向的递归传来了true的信号,我们就返回消息true,传上去!这条路可以走!
  8. 如果四个方向都没有路(元素不符合要求),就在后撤过程中把标记清除,避免妨碍其他路径的搜索。然后传回消息,此路不通(返回false)!

注意事项
(我踩的一些很低级的坑)

1、可以加入一些判断条件进行优化,使搜索的途径减少,也就是剪枝,代码中对矩阵元素个数与字母个数先进行一次比较,就算是一个小优化。
2、用递归做判断条件时,请返回true,不要返回false。
3、不要用 i++,++i 等作为形参传入函数,直接使用 i+1即可,否则可能会改变外层作用域中 i 的值,影响到其他路径的判断。


四、总结

深搜常常需要用到递归,所以要先对递归有着熟练的掌握,然后思考各种边界值的判定,当然,类似的深搜的题目都有固定的套路,最好先找出其中的固定规律,之后套模板就轻松多了。这道题遍历每个元素,对每个可能的元素向下挖掘所有的可能性,直到搜索完毕再进行下一个元素的搜索,这就是前面介绍的深搜思想的应用。
希望本篇题解能对大家有所帮助

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值