Javascript 使用矩阵的正确方法是什么?

Javascript 使用矩阵的正确方法是什么?,javascript,algorithm,recursion,depth-first-search,Javascript,Algorithm,Recursion,Depth First Search,我做这个练习题是为了找出矩阵中是否存在一个单词,这让我意识到我并不完全理解DFS 在破解编码面试时,DFS的伪代码为: void search(Node root) { if (root == null) return; visit(root); root.visited = true; for each (Node n in root.adjacent) { if (n.visited == false) { search(n); } } }

我做这个练习题是为了找出矩阵中是否存在一个单词,这让我意识到我并不完全理解DFS

在破解编码面试时,DFS的伪代码为:

void search(Node root) {
  if (root == null) return;
  visit(root);
  root.visited = true;
  for each (Node n in root.adjacent) {
    if (n.visited == false) {
      search(n);
    }
  }
}
对我来说,这看起来像是一种格式:

  • 目标
  • 标记
  • 环路
  • 如果邻居不符合条件,就提前保释
  • 重现
  • 因此,使用这种格式,我编写了函数
    dfs()

    这更简洁,但对我来说更难理解。第一个版本
    dfs()
    执行循环,并在递归之前提前释放邻居,这对我来说更有意义。第二个版本没有循环,因此它在当前节点上执行所有检查

    我注意到的第一件事是,在大多数涉及网格的问题中,解决方案都涉及递归后的“取消标记”。为什么会这样?这是否仅适用于“单词搜索问题”等特定情况,您可能希望将来以其他路径重新访问节点?

    哪个是正确的,
    dfs()
    dfs2()


    下面是全部内容:

    var dirs = [
      [0,1],  // r
      [1,0],  // d
      [0,-1], // u
      [-1,0], // l
    ];
    
    var wsBoard = [
      ['A','B','C','E'],
      ['S','F','C','S'],
      ['A','D','E','E']
    ];
    
    var exist = function(board, word, version) {
      for (var r = 0; r < board.length; r++) {
        for (var c = 0; c < board[0].length; c++) {
          if (board[r][c] === word[0])
            if (dfs(r, c, 0)) return true;
            // if (dfs2(r, c, 0)) return true;
        }
      }
    
      return false;
    
      function dfs(r, c, i) {
        console.log(`(${r},${c})\t${i}: ${word[i]}`);
    
        // goal
        if (i === word.length-1) return true;
    
        // mark
        board[r][c] = '#';
    
        // loop and recurse each neighbor
        for (var d of dirs) {
          var nr = r + d[0];
          var nc = c + d[1];
    
          // bail early if neighbor does not meet conditions
          if (nr < 0 || nc < 0 || nr >= board.length || nc >= board[0].length) continue;  // neighbor is out of bounds
          if (board[nr][nc] === '#') continue;                                            // neighbor already visited
          if (board[nr][nc] !== word[i+1]) continue;                                      // neighbor does not meet goal
    
          console.log(board);
    
          // recursion
          var result = dfs(nr, nc, i+1);
    
          // un-mark
          board[r][c] = word[i];
    
          return result;
        }
      }
    
      function dfs2(r, c, i) {
        console.log(`(${r},${c})\t${i}: ${word[i]}`);
    
        // goal
        if (i === word.length) return true;
    
        // bail early if current does not meet conditions
        if (r < 0 || c < 0 || r >= board.length || c >= board[0].length) return false;  // current is out of bounds
        if (board[r][c] === '#') return false;                                          // current already visited
        if (board[r][c] !== word[i]) return false;                                      // current does not meet goal
    
        // mark
        board[r][c] = '#';
    
        console.log(board);
    
        // recursion
        var result = dfs2(r+1, c, i+1) || dfs2(r-1, c, i+1) || dfs2(r, c+1, i+1) || dfs2(r, c-1, i+1);
    
        // un-mark
        board[r][c] = word[i];
    
        return result;
      }
    };
    
    console.log(exist(wsBoard, 'ABCCED'));  // => true
    console.log(exist(wsBoard, 'SEE'));     // => true
    console.log(exist(wsBoard, 'ABCB'));    // => false
    
    var-dirs=[
    [0,1],//r
    [1,0],//d
    [0,-1],//u
    [-1,0],//l
    ];
    var wsBoard=[
    [A'、'B'、'C'、'E'],
    [S'、'F'、'C'、'S'],
    ['A','D','E','E']
    ];
    var exist=功能(板、字、版本){
    对于(var r=0;r=board.length | | nc>=board[0].length)继续;//邻居超出范围
    如果(线路板[nr][nc]='#')继续;//已访问邻居
    如果(board[nr][nc]!==word[i+1])继续;//邻居未达到目标
    控制台.日志(板);
    //递归
    var结果=dfs(nr、nc、i+1);
    //un标记
    板[r][c]=字[i];
    返回结果;
    }
    }
    函数dfs2(r、c、i){
    log(`(${r},${c})\t${i}:${word[i]}`);
    //目标
    如果(i==word.length)返回true;
    //如果电流不符合条件,则提前保释
    如果(r<0 | | c<0 | | r>=board.length | | c>=board[0].length)返回false;//当前值超出范围
    如果(线路板[r][c]='#')返回false;//当前已访问
    if(board[r][c]!==word[i])返回false;//当前未达到目标
    //标记
    董事会[r][c]=“#”;
    控制台.日志(板);
    //递归
    var结果=dfs2(r+1,c,i+1)| | dfs2(r-1,c,i+1)| | dfs2(r,c+1,i+1)| | dfs2(r,c-1,i+1);
    //un标记
    板[r][c]=字[i];
    返回结果;
    }
    };
    console.log(存在(wsBoard,'ABCCED'));//=>真的
    log(存在(wsBoard,'SEE');//=>真的
    console.log(存在(wsBoard,'ABCB'));//=>假的
    
    我相信,尽管
    dfs
    dfs2
    都基于相同的理念
    dfs
    有一个缺陷,但它只返回第一条路径的探索结果

    看看这个例子,我试图在黑板上找到
    FOO
    ,很明显,它是第一列,但是您的实现返回
    false

    var-dirs=[
    [0,1],//r
    [1,0],//d
    [0,-1],//u
    [-1,0],//l
    ];
    var板=[
    [F'、'O'、'X'],
    [O','','',],
    ['O','',']
    ];
    var exist=函数(字){
    功能dfs(r、c、i){
    //标记
    董事会[r][c]=“#”;
    //目标
    如果(i==word.length-1)返回true;
    //循环并递归每个邻居
    for(dirs的var d){
    var-nr=r+d[0];
    var nc=c+d[1];
    //若邻居不符合条件,则提前保释
    如果(nr<0 | | nc<0 | | nr>=board.length | | nc>=board[0].length)继续;//邻居超出范围
    如果(线路板[nr][nc]='#')继续;//已访问邻居
    如果(board[nr][nc]!==word[i+1])继续;//邻居未达到目标
    //递归
    var结果=dfs(nr、nc、i+1);
    //un标记
    板[r][c]=字[i];
    返回结果;
    }
    }
    对于(var r=0;rlog(exist('FOO'))
    太棒了!是的,我开始做后续问题“给定一本字典查找所有单词”(DFS和Trie),并意识到我很容易用
    dfs2()
    ,但它一直用
    DFS()
    失败。我以为我走远了,但那是你发现的虫子。谢谢还感谢您对取消标记的深入了解。对我来说不是那么直观,所以我会继续练习。注意到了其他的东西。
    取消标记不应该发生在for循环之外吗?So
    mark;对于(){}取消标记
    function dfs2(r, c, i) {    
    // goal
    if (i === word.length) return true;
    
    // bail early if current does not meet conditions
    if (r < 0 || c < 0 || r >= board.length || c >= board[0].length) return false;  // current is out of bounds
    if (board[r][c] === '#') return false;                                          // current already visited
    if (board[r][c] !== word[i]) return false;                                      // current does not meet goal
    
    // mark
    board[r][c] = '#';
    
    // recursion
    var result = dfs2(r+1, c, i+1) || dfs2(r-1, c, i+1) || dfs2(r, c+1, i+1) || dfs2(r, c-1, i+1);
    
    // un-mark
    board[r][c] = word[i];
    
    return result;
    }
    
    var dirs = [
      [0,1],  // r
      [1,0],  // d
      [0,-1], // u
      [-1,0], // l
    ];
    
    var wsBoard = [
      ['A','B','C','E'],
      ['S','F','C','S'],
      ['A','D','E','E']
    ];
    
    var exist = function(board, word, version) {
      for (var r = 0; r < board.length; r++) {
        for (var c = 0; c < board[0].length; c++) {
          if (board[r][c] === word[0])
            if (dfs(r, c, 0)) return true;
            // if (dfs2(r, c, 0)) return true;
        }
      }
    
      return false;
    
      function dfs(r, c, i) {
        console.log(`(${r},${c})\t${i}: ${word[i]}`);
    
        // goal
        if (i === word.length-1) return true;
    
        // mark
        board[r][c] = '#';
    
        // loop and recurse each neighbor
        for (var d of dirs) {
          var nr = r + d[0];
          var nc = c + d[1];
    
          // bail early if neighbor does not meet conditions
          if (nr < 0 || nc < 0 || nr >= board.length || nc >= board[0].length) continue;  // neighbor is out of bounds
          if (board[nr][nc] === '#') continue;                                            // neighbor already visited
          if (board[nr][nc] !== word[i+1]) continue;                                      // neighbor does not meet goal
    
          console.log(board);
    
          // recursion
          var result = dfs(nr, nc, i+1);
    
          // un-mark
          board[r][c] = word[i];
    
          return result;
        }
      }
    
      function dfs2(r, c, i) {
        console.log(`(${r},${c})\t${i}: ${word[i]}`);
    
        // goal
        if (i === word.length) return true;
    
        // bail early if current does not meet conditions
        if (r < 0 || c < 0 || r >= board.length || c >= board[0].length) return false;  // current is out of bounds
        if (board[r][c] === '#') return false;                                          // current already visited
        if (board[r][c] !== word[i]) return false;                                      // current does not meet goal
    
        // mark
        board[r][c] = '#';
    
        console.log(board);
    
        // recursion
        var result = dfs2(r+1, c, i+1) || dfs2(r-1, c, i+1) || dfs2(r, c+1, i+1) || dfs2(r, c-1, i+1);
    
        // un-mark
        board[r][c] = word[i];
    
        return result;
      }
    };
    
    console.log(exist(wsBoard, 'ABCCED'));  // => true
    console.log(exist(wsBoard, 'SEE'));     // => true
    console.log(exist(wsBoard, 'ABCB'));    // => false