【问题标题】:time limit exceeded for algorithm question in Java "Number of Islands"Java“岛屿数”中的算法问题超出了时间限制
【发布时间】:2022-01-17 12:35:03
【问题描述】:

对于 leetcode 200 Number of Islands,这个解决方案可以正常工作。但是,对于较低的解决方案,由于超出时间限制,提交失败。据我了解,两种解决方案都应该同时运行。你能帮忙吗?

class Solution {
    // BFS time O(mn) | space O(min(m,n))
    public int numIslands(char[][] grid) {
        int islandsCount = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == '1') {
                    islandsCount++;
                    bfs(grid, i, j);
                }
            }
        }
        return islandsCount;
    }

    public static void bfs(char[][] grid, int row, int col) {
        int rc = grid.length;
        int cc = grid[0].length;
        Deque<Integer> queue = new ArrayDeque<Integer>();
        queue.offer(row*cc + col);
        while (!queue.isEmpty()) {
            Integer spotIdx = queue.poll();
            int i = spotIdx/cc, j = spotIdx%cc;
            // grid[i][j] = '0';
            if (i-1 >= 0 && grid[i-1][j] == '1') {
                queue.offer((i-1)*cc+j);
                grid[i-1][j] = '0';
            }
            if (i+1 < rc && grid[i+1][j] == '1') {
                queue.offer((i+1)*cc+j);
                grid[i+1][j] = '0';
            }
            if (j-1 >= 0 && grid[i][j-1] == '1') {
                queue.offer(i*cc+(j-1));
                grid[i][j-1] = '0';
            }
            if (j+1 < cc && grid[i][j+1] == '1') {
                queue.offer(i*cc+(j+1));
                grid[i][j+1] = '0';
            }
        }
        return;
    }
}

以下版本会抛出超过时间限制,但在某些测试用例中仍然给出正确答案。

class Solution {
    // BFS time O(mn) | space O(min(m,n))
    public int numIslands(char[][] grid) {
        int islandsCount = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == '1') {
                    islandsCount++;
                    bfs(grid, i, j);
                }
            }
        }
        return islandsCount;
    }

    public static void bfs(char[][] grid, int row, int col) {
        int rc = grid.length;
        int cc = grid[0].length;
        Deque<Integer> queue = new ArrayDeque<Integer>();
        queue.offer(row*cc + col);
        while (!queue.isEmpty()) {
            Integer spotIdx = queue.poll();
            int i = spotIdx/cc, j = spotIdx%cc;
            grid[i][j] = '0';
            if (i-1 >= 0 && grid[i-1][j] == '1') {
                queue.offer((i-1)*cc+j);
            }
            if (i+1 < rc && grid[i+1][j] == '1') {
                queue.offer((i+1)*cc+j);
            }
            if (j-1 >= 0 && grid[i][j-1] == '1') {
                queue.offer(i*cc+(j-1));
            }
            if (j+1 < cc && grid[i][j+1] == '1') {
                queue.offer(i*cc+(j+1));
            }
        }
        return;
    }
}

【问题讨论】:

  • 第一个代码将网格元素写入队列时将其标记为已访问。当它们从队列中读取时,第二个代码将它们标记为已访问。所以问题是在第二个代码中,同一个元素可以多次写入队列。证明这一点的简单方法是添加一个int q_count 变量,该变量在每次调用queue.offer 时递增。然后在 BFS 完成后打印计数。

标签: java algorithm time-complexity breadth-first-search


【解决方案1】:

看看这个特定的输入:

0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0

第一次调用 bfs 时,它会获取 1 和 1 作为 ij 的值。

队列接收它再次拉取的坐标 (1, 1)。

单元格被清除,所以我们得到:

0 0 0 0
0 0 1 0
0 1 1 0
0 0 0 0

那么四个if条件中有两个为真,队列接收坐标(2, 1)和(1, 2)。

在第二次迭代中,(2, 1) 从队列中弹出,单元格被清除:

0 0 0 0
0 0 1 0
0 0 1 0
0 0 0 0

只有一个if 条件为真,并且 (2, 2) 正在推入队列,队列现在有 (1, 2) 和 (2, 2)。

然后在第三次迭代中 (1, 2) 从队列中弹出,并且单元格被清除:

0 0 0 0
0 0 0 0
0 0 1 0
0 0 0 0

一个if 条件为真,(2, 2) 被推入队列。但是现在我们有 (2, 2) 两次在队列中!

所以在第四次迭代中,(2, 2) 被弹出并清除:

0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

没有if 条件为真。

第五次迭代会再次弹出(2, 2),但是已经被清除并处理过了,所以这是一次无用的迭代。

这只是一个简单的例子,但在更大的网格中,队列中“重复”坐标的数量会迅速增加,它们都代表了一次迭代,该迭代将会发生……什么都没有。这是浪费时间。显然,测试套件包括由于这种无用的开销而使您的函数超时的情况。

您的代码的第一个版本不允许重复进入队列。

【讨论】:

  • 非常感谢!这个解释很清楚~
猜你喜欢
  • 2023-03-28
  • 1970-01-01
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 2019-08-12
  • 1970-01-01
  • 1970-01-01
  • 2021-03-12
相关资源
最近更新 更多