7-LeetCode剑指 Offer 12. 矩阵中的路径

369 阅读2分钟

一、题目

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。

leetcode7.jpg

样例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题解,思路参照leetcode-cn.com/problems/ju…

理一下总体思路,首先此题本质上就是搜索一个矩阵,然后找到里面是否存在一个单词和word中的一样,那么我们肯定先要把字符串word化成一个字符数组wordArray,然后用wordArray[k]和board[i][j]逐一比较,然后相等则k++,最后看k是否等于wordArray.length-1,相等则全部比对完毕,返回true。

那怎么“搜索”这个矩阵就成了关键问题:题解给的方法是dfs(深度优先搜索),通过递归,先朝一个方向搜到不能再搜,然后再回来,再换一个方向。然后在遇到匹配不成功的情况时候,就要直接返回false,结束这个方向的搜索。

三、代码(js)

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
//  split功能测试
//  let word = "abcdefghijk"
//  let words = word.split("");
//  console.log(words);

//  样例测试
//  let board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]];
//  let word = "ABCCED";
 
 var exist = function(board, word) {
   //先把word字符串转换为字符数组 用split函数
   let wordArray = word.split("");
   for(let i = 0 ,rows = board.length; i < rows; i++)
   {
       for(let j =0,cols = board[0].length ;j< cols ;j++)
       {
           if(dfs(board,wordArray,i,j,0)) return true
       }
   }
   return false
};

 let dfs = function(board,wordArray,i,j,k)
 {
    //当数组超限,或者字符不匹配(board[i][j]不等于wordArray[k])时,直接返回false
    if(i<0||i>board.length-1||j<0||j>board[0].length-1||board[i][j]!=wordArray[k])
      return false;
    //当此时字符匹配 且 此时k==wordArray.length-1时,就说明wordArray的全部字符匹配成功,返回true 
    if(k==wordArray.length-1)
      return true;
    //当此时字符匹配 且 wordArray的字符还没有匹配完时,就递归继续往深处搜索

    //暂时先赋值0再往深处找,避免回头找、避免重复使用  (标记为已访问)
    board[i][j] = '\0';
    let res = dfs(board,wordArray,i+1,j,k+1)
        ||dfs(board,wordArray,i-1,j,k+1)
        ||dfs(board,wordArray,i,j+1,k+1)
        ||dfs(board,wordArray,i,j-1,k+1) 
    //这条路的最深处的每个方向查找完之后,回溯这个值,这样能找到回去的路 (相当于取消标记)
    board[i][j]=wordArray[k];
    return res;
 }

//  输出测试
//  console.log(exist(board,word));

四、拓展思考

讲几个要注意的点:

1.字符串word先改成字符数组wordArray

2.一开始我对于这段代码的理解有点误解吗,我以为是先从起点遍历一周再往深走,这是大错特错的!其实是先从起点直接先走到底,走到底之后再遍历四周,遍历这个节点的四周之后再回溯,然后再遍历上一个节点的四周,再回溯,最后回到起点。

    let res = dfs(board,wordArray,i+1,j,k+1)
    ||dfs(board,wordArray,i-1,j,k+1)
    ||dfs(board,wordArray,i,j+1,k+1)
    ||dfs(board,wordArray,i,j-1,k+1) 

3.感觉这段代码还是有点难理解,个人理解就是board[i][j]='\0'就是将当前这个值标记为已访问,这样就不会出现重复使用的情况,然后当遍历完这个值的四周之后,再把当前这个值值赋回去(board[i][j]=wordArray[k]),相当于取消标记了。

board[i][j] = '\0';
let res = dfs(board,wordArray,i+1,j,k+1)
    ||dfs(board,wordArray,i-1,j,k+1)
    ||dfs(board,wordArray,i,j+1,k+1)
    ||dfs(board,wordArray,i,j-1,k+1) 
board[i][j]=wordArray[k];

题目来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ju…