主题:使用回溯(而不仅仅是递归DFS)背后的直觉

时间:2019-04-04 14:28:48

标签: java algorithm depth-first-search backtracking recursive-backtracking

对于初学者来说,我并不是想问the difference between a car and a DeLorean。因此,我正在解决this LeetCode问题:

  

给出一个2D板和一个单词,查找单词是否存在于网格中。   该单词可以由顺序相邻的单元格的字母构成,其中“相邻”单元格是水平或垂直相邻的单元格。同一字母单元不得重复使用。

     

board =
  [
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
  ]

     

给出单词=“ ABCCED”,返回true。

highly upvoted solution如下:

public class Solution {
public boolean exist(char[][] board, String word) {
    for(int i = 0; i < board.length; i++)
        for(int j = 0; j < board[0].length; j++){
            if(exist(board, i, j, word, 0))
                return true;
        }
    return false;
}
private boolean exist(char[][] board, int i, int j, String word, int ind){
    if(ind == word.length()) return true;
    if(i > board.length-1 || i <0 || j<0 || j >board[0].length-1 || board[i][j]!=word.charAt(ind))
        return false;
    board[i][j]='*';
    boolean result =    exist(board, i-1, j, word, ind+1) ||
                        exist(board, i, j-1, word, ind+1) ||
                        exist(board, i, j+1, word, ind+1) ||
                        exist(board, i+1, j, word, ind+1);
    board[i][j] = word.charAt(ind);     //--> why?
    return result;
}

我的问题是-与使用正常的递归DFS相比,对此问题使用回溯算法的直觉是什么?在使用递归DFS时,我只需将节点标记为已访问,然后再移至其邻居(从而找出ABCCED是有效路径)。为什么我必须 backtrack (上面代码中的注释行)来了解该路径是否存在?

谢谢!

编辑:以另一种方式问我的问题:我们为什么不从最上方的左单元格A开始,然后开始使用{{1}设置标记访问节点的方法?在下一次迭代中,我们可以从最左visited-A相邻的单元格开始,使用新的B设置来标记访问的节点来访问其所有邻居,等等?为什么要使用回溯?

2 个答案:

答案 0 :(得分:0)

深度优先搜索是一种回溯算法。 递归的本质是回溯机制本身。如果该路径不是好的路径,则在进入更深的树之前会返回false。 这是您的回溯:

if(i > board.length-1 || i <0 || j<0 || j >board[0].length-1 || board[i][j]!=word.charAt(ind))
    return false;

board[i][j] = word.charAt(ind);

仅用于将节点重置回其原始值,并允许其在Bakon Jarser在问题文章中评论时在其他相邻路径中使用。

您可以快速查看first paragraphthis post

希望获得帮助。

答案 1 :(得分:0)

假设您正在此板上寻找ABCBDE一词:

ABD
BCE

假设与您提供的源代码中的邻居探索顺序相同,您的DFS首先将尝试使用right-> down-> left路径,因此您的visited集将包含最左边的2x2正方形,您将无法找到解决方案。