【问题标题】:Stopping condition for simple DFS traversing简单 DFS 遍历的停止条件
【发布时间】:2012-08-22 08:23:53
【问题描述】:

我正在尝试解决这个problem on topcoder 作为一种实践,我尝试为它实现 DFS 解决方案,它运行良好,除了一个错误:它会不断遍历每个未访问的单元格,即使它到达死胡同,初始代码是:

public void func(int[][] x, StringBuilder s, int i, int j, int n) {
    if (i < 0 || j < 0 || i > n || j > n || x[i][j] == 1) {
        return;
    }
    s.append((char) (97 + j));
    s.append(n - i + 1 + "-");
    x[i][j] = 1;
    func(x, s, i, j + 1, n);
    func(x, s, i - 1, j, n);
    func(x, s, i + 1, j, n);
    func(x, s, i, j - 1, n);
    return;
}

public String dukePath(int n, String initPosition) {
    String p = initPosition;
    int x[][] = new int[n][n];
    StringBuilder s = new StringBuilder("");
    func(x, s, n - Integer.parseInt(p.charAt(1) + ""), (int) p.charAt(0) - 97, n - 1);
    s.replace(s.length() - 1, s.length(), "");
    if (s.length() > 40) {
        s.replace(20, s.length() - 20, "...");
    }
    return s.toString();
}

所以我尝试通过添加一个布尔标志和初始位置 (z,y) 来修改函数“func()”的签名,以将当前单元格与 ;如果代码尝试重新访问初始位置,那么它应该返回,但它也没有工作..

当再次到达死路或初始位置时,如何停止遍历?

【问题讨论】:

  • 当你到达死胡同时,你正在标记x[i][j] = 1;,但当你回溯时,你并没有将它标记为未访问x[i][j] = 0;。
  • 我只是希望它在到达死胡同时停止,而不是探索另一个单元格,所以我想不需要回溯
  • 尝试不使用 StringBuilder 并传递常规字符串。您可能还需要返回 String。
  • @a.u.r 你说的“死胡同”是什么意思?目前你总是检查所有四个相邻的位置......
  • @veredesmarald 我的意思是到达所有相邻单元格都被访问的位置

标签: java algorithm depth-first-search


【解决方案1】:

您错误地处理了这个问题,您需要做的就是在每一步都移动到词典上最大的邻居,直到您到达访问所有相邻单元格的点。这是简单的迭代,不需要 DFS:

public static String dukePath(int n, String initPosition) {
    int x = initPosition.charAt(0) - 'a';
    int y = n - (initPosition.charAt(1) - '0');
    boolean grid[][] = new boolean[n][n];
    StringBuilder s = new StringBuilder(initPosition);

    while (true) {
        grid[x][y] = true;

        if (x < (n - 1) && !grid[x + 1][y])
            x++; // Right
        else if (y > 0 && !grid[x][y - 1])
            y--; // Up
        else if (y < (n - 1) && !grid[x][y + 1])
            y++; // Down
        else if (x > 0 && !grid[x - 1][y])
            x--; // Left
        else
            break; // Nowhere left to go!

        s.append("-" + (char)('a' + x) + (char)('0' + n - y));
    }

    if (s.length() > 40) {
        s.replace(20, s.length() - 20, "...");
    }

    return s.toString();
}

public static void main(String args[])
{
    System.out.println(dukePath(3, "b2"));
    System.out.println(dukePath(4, "d4"));
    System.out.println(dukePath(3, "a2"));
    System.out.println(dukePath(4, "d3"));
    System.out.println(dukePath(8, "a8"));
}

给出预期的结果:

b2-c2-c3-b3-a3-a2-a1-b1-c1
d4-d3-d2-d1-c1-c2-c3...b3-b2-b1-a1-a2-a3-a4
a2-b2-c2-c3-b3-a3
d3-d4-c4-c3-c2-d2-d1...b2-b3-b4-a4-a3-a2-a1
a8-b8-c8-d8-e8-f8-g8...a1-a2-a3-a4-a5-a6-a7

【讨论】:

  • 我之前知道迭代解决方案,但我尝试使用DFS来实现它。如您所见,我的方法几乎相同:偏向正确的方向,然后向上,等等..等等..我所做的只是交换 Xs 和 Ys。也许我没有澄清我的问题:对于第三个示例,我的代码返回:a2-b2-c2-c3-b3-a3-c1-b1-a1 而不是 a2-b2-c2-c3-b3-a3 with extra单元格“c1-b1-a1”
  • @a.u.r 这就是 DFS 的工作原理。一旦它到达一个不能再继续的点,它就会回溯到它可以继续的点。 DFS 并非设计为在第一次遇到终端节点时停止,您可以对其进行修改以使其停止,但目的是什么?你只是添加了一堆没有任何作用的代码。
  • @verdesmarald 我相信你是对的,现在我明白了。另一个问题:如果我有一个目标单元格,那么当我到达那个单元格时如何终止代码?
  • @a.u.r 您可以执行类似修改原始func 以返回boolean 的操作。在您访问的每个单元格中,如果它与目标条件匹配,则返回 true,并确保将 true 传播到调用堆栈,而不是继续到下一个邻居。请注意,如果存在多个目标路径,这将为您提供 DFS 找到的第一个路径,不一定是最佳路径。
【解决方案2】:

我认为最简单的解决方案是创建一组包含 i 和 j 的对象。在每次调用 func 时,如果此集合包含具有 i 和 j 的目标对象,则不应调用 func 函数。这种方法既涵盖了step backs,也涵盖了loops。

【讨论】:

  • 我尝试通过添加初始位置作为参数,并将当前单元格与其进行比较;决定是否停止,但它不起作用: public void func(int[][] x , StringBuilder s,int i , int j , int n , boolean f , int z ,int y){ // z & y 是问题中给出的初始坐标 if(i==z && j==y && x[z][y]==1) f=true;如果(f==true)返回; // 其余代码同上
  • 这不起作用,因为您的程序可能会在不包含原始位置的 xy 空间中陷入循环。一般来说,当你调用这些 func 函数 4 次时,你需要检查目标位置是否之前没有见过。
  • 不,即使它到达初始位置并将布尔值“f”更改为 true,它也不起作用
  • 这就是我说的,它不起作用。因为有很多其他的循环路径不包含初始位置,它们将永远运行。如果您只想使用一个线程运行问题,veredesmarald 的解决方案更简单,并且没有堆栈溢出问题。否则,如果您坚持使用递归解决方案,则需要这组可见位置。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-09
  • 2018-03-09
  • 1970-01-01
  • 2017-02-25
  • 2021-08-20
  • 2017-02-12
相关资源
最近更新 更多