【问题标题】:DFS method not producing expected outputDFS 方法没有产生预期的输出
【发布时间】:2021-04-05 21:24:53
【问题描述】:

所以目标是使用 dfs 打印所有可能的数字,其数字可以由板上的一系列相邻字符组成。相邻是指彼此相邻,垂直、水平或对角线。总共有 8 个运动方向。例如:下面的板

1 2
3 4

为了简单起见,假设我们总是从第 0 行第 0 列开始,即 1。所以可能的数字是:1、12、13、14、123、124、132、134、142、143、1234等(这种情况非常简单,因为所有数字都直接相邻) 所以我有以下代码:

static char[][] grid;
static int rows;
static int columns;

public static void main(String[] args) {
    //test grid. made it small so that solutions are easy to count
    grid = new char[][]{
            new char[]{'1', '2'},
            new char[]{'3', '4'},
    };
    rows = grid.length;
    columns = grid[0].length;
    dfs(0, 0, new boolean[rows][columns], new StringBuilder());
}

public static void dfs(int row, int col, boolean[][] visited, StringBuilder builder){
    visited[row][col] = true;
    builder.append(grid[row][col]);
    System.out.println(builder.toString());
    //the 2 loops are for the 8 movement directions
    for(int x = -1; x <= 1; x++){
        for(int y = -1; y <= 1; y++){
            //index bounds check
            if((row + x >= 0) && (row + x < rows) && (col + y >= 0) && (col + y < columns)){
                if(!visited[row + x][col + y]){
                    dfs(row + x, col + y, visited, builder);
                    // I tried changing it so that it passes a COPY of 'visited' and 'builder',
                    // but output still the same
                    dfs(row + x, col + y, Arrays.copyOf(visited, visited.length), new StringBuilder(builder));
                }

            }
        }
    }
}

我的代码输出不正确。这是它打印的内容:

1
12
123
1234

这些数字是解决方案的一部分,但数量远不止于此。该错误可能与将同一对象传递给递归 dfs() 调用有关,但我尝试更改它以便它只复制对象,并且它仍然提供相同的输出。 有什么想法吗?

【问题讨论】:

  • 我不确定Arrays.copyOf() 在二维数组上的表现如何,但几乎可以肯定它不会像您认为的那样做。请参阅How do I copy a 2 Dimensional array in Java?
  • 幸运的是,之前有人问过similar question,所以我不需要研究! ^^
  • @JanezKuhar 这很有趣......制作了一个手动二维数组副本,它现在可以工作了。谢谢 :)

标签: java algorithm depth-first-search


【解决方案1】:

您应该能够在不创建已访问矩阵的副本的情况下实现预期的输出。

重要的是在每次 COMPLETE 访问后将 visited[row][col] 设置为 false

例子:

假设您已经遍历了“12”,现在还有“3”和“4”可以访问。在当前状态下,visited 矩阵的“1”和“2”值设置为真,“3”和“4”设置为假。生成所有以“12”开头的字符串后,您将开始查看所有以“13”开头的字符串。

但是看看这里发生了什么!您已将“2”的访问值设置为 true,因此以后不会生成包含“2”的字符串。

这就是为什么您必须在从该点开始生成所有路径后将visited[row][col] 设置为 false。

要真正理解这一点,请用笔和纸跟踪您的代码,您会更好地记住它。

注意:

实际上,在上述示例中,您永远不会生成以 '13' 开头的字符串,因为 visited[1][1] 之前已设置为 true,因此再也不会达到 3。我忽略了这个事实来说明一点。

为什么我建议不要对每个递归调用都进行深拷贝?简单,节省时间和空间。每次调用都需要 O(m*n) 的空间和时间来生成和存储一个新的访问矩阵。

这是正确代码的一种变体:

import java.util.*;

public class DFS{

    static char[][] grid;
    static int rows;
    static int columns;

    public static void main(String[] args) {
        //test grid. made it small so that solutions are easy to count
        grid = new char[][]{
                new char[]{'1', '2'},
                new char[]{'3','4'},
        };
        rows = grid.length;
        columns = grid[0].length;
        ArrayList<String>list = new ArrayList<>();
        dfs(0, 0, new boolean[rows][columns], new StringBuilder());
        //System.out.println(list);
    }

    public static void dfs(int row, int col, boolean[][] visited, StringBuilder builder){
        visited[row][col] = true;
        builder.append(grid[row][col]);
        System.out.println(builder);

        for(int x = -1; x <= 1; x++){
            for(int y = -1; y <= 1; y++){
                //index bounds check
                if((row + x >= 0) && (row + x < rows) && (col + y >= 0) && (col + y < columns)){
                    if(!visited[row + x][col + y]){
                        dfs(row + x, col + y, visited, builder);
                    }

                }
            }
        }
        visited[row][col]=false;
        builder.deleteCharAt(builder.length()-1);
    }
}

编辑:我忘记突出显示在删除当前StringBuilder 的最后一个字符的位置添加的最后一行代码。这是回溯位,因此在生成所有“12xx”字符串后,删除“2”,将其替换为“3”,然后开始探索“13xx”字符串系列。

【讨论】:

    猜你喜欢
    • 2016-10-03
    • 1970-01-01
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    相关资源
    最近更新 更多