leetcode
leetcode 701 ~ 750
打砖块

打砖块

难度:

标签:

题目描述

代码结果

运行时间: 167 ms, 内存: 23.2 MB


/* 
* Solution approach using Java Streams: 
* Similar to the previous solution but leveraging Java Streams for iteration and collection operations. 
*/

import java.util.*;
import java.util.stream.*;

public class SolutionStream {
    public int[] hitBricks(int[][] grid, int[][] hits) {
        int m = grid.length, n = grid[0].length;
        Arrays.stream(hits).forEach(hit -> grid[hit[0]][hit[1]] -= 1);
        IntStream.range(0, n).forEach(j -> dfs(grid, 0, j));
        int[] res = new int[hits.length];
        IntStream.range(0, hits.length).map(i -> hits.length - 1 - i).forEach(i -> {
            int r = hits[i][0], c = hits[i][1];
            grid[r][c] += 1;
            if (grid[r][c] == 1 && isConnectedToTop(grid, r, c)) {
                res[i] = dfs(grid, r, c) - 1;
            }
        });
        return res;
    }

    private int dfs(int[][] grid, int r, int c) {
        if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length || grid[r][c] != 1) {
            return 0;
        }
        grid[r][c] = 2;
        return 1 + IntStream.of(new int[]{1, -1}).map(d -> dfs(grid, r + d, c) + dfs(grid, r, c + d)).sum();
    }

    private boolean isConnectedToTop(int[][] grid, int r, int c) {
        if (r == 0) {
            return true;
        }
        return Stream.of(new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}).anyMatch(dir -> {
            int x = r + dir[0], y = c + dir[1];
            return x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == 2;
        });
    }
}

解释

方法:

这道题的思路是使用并查集和逆向思维。首先,我们将所有的hit操作预先执行,将对应位置的砖块标记为已消除状态。然后从网格的顶部开始进行深度优先搜索,将所有与顶部连通的砖块标记为稳定状态。接下来,我们逆序遍历hits数组,依次将砖块还原。对于每个还原的砖块,我们判断其是否与稳定的砖块相邻或者位于顶部,如果是,则进行深度优先搜索,将与其连通的不稳定砖块都标记为稳定状态,并记录下变为稳定状态的砖块数量。最后返回每次还原操作导致掉落的砖块数量。

时间复杂度:

O(l * nm)

空间复杂度:

O(nm)

代码细节讲解

🦆
为什么在处理结束后,需要将grid中的值从1改为2来标记砖块为稳定状态?这与原始的1有何不同?
在算法中,将grid中的值从1改为2是用来区分未访问的砖块和已确认为稳定的砖块。原始的1表示砖块存在但尚未确认其稳定性,而2表示该砖块已经被访问过,并且确认与顶部直接或间接相连,因此是稳定的。这种区分有助于在后续的递归搜索中避免重复访问同一个砖块,从而优化算法的效率。
🦆
在预处理`hits`数组时,为何直接将grid中的值减1而不是检查当前位置是否有砖块存在?
算法预设所有hits操作都是有效的,即hits数组中的每个位置都有砖块存在。因此,在预处理阶段,直接将对应位置的砖块值减1(即从1变为0)以模拟砖块被击中并消除的效果。这个假设简化了代码的复杂度,避免了不必要的检查。如果有必要处理无效的hits(比如原位置没有砖块的情况),则需要在代码中添加额外的逻辑来验证每个hit的有效性。
🦆
在计算每次还原操作导致的掉落砖块数量时,为什么要从结果中减去1?
在每次还原操作中,我们首先检查被还原的砖块是否能与稳定砖块相连。如果可以,我们通过深度优先搜索(DFS)将所有与之相连的不稳定砖块标记为稳定。在这个过程中,DFS函数返回的是所有通过此次搜索变为稳定的砖块数量,包括被还原的那一块砖本身。因此,为了准确计算由于这次还原操作额外导致的掉落(变稳定)的砖块数量,需要从DFS的结果中减去1,即除去被还原的那块砖本身。
🦆
递归深度优先搜索(DFS)中,如何处理grid边界外的情况以及避免重复访问已标记为稳定的砖块?
在递归深度优先搜索中,首先会检查当前坐标是否越界(即是否超出grid的行或列范围),以及当前位置的砖块状态。如果当前位置越界或者不是未访问的砖块(即状态不是1),则DFS直接返回0并终止当前路径的搜索。同时,为了避免重复访问已经标记为稳定的砖块(状态为2),一旦某个砖块在DFS过程中被验证为稳定,其状态就会从1变为2。这样,后续的DFS调用遇到状态为2的砖块时,会直接返回0,从而有效避免重复访问。这些措施确保了DFS的效率和正确性。

相关问题