leetcode
leetcode 601 ~ 650
不同岛屿的数量 II

不同岛屿的数量 II

难度:

标签:

题目描述

代码结果

运行时间: 130 ms, 内存: 17.9 MB


/*
题目: 不同岛屿的数量 II
 
思路:
1. 使用深度优先搜索(DFS)遍历网格中的每一个陆地单元格,标记访问过的单元格。
2. 在DFS过程中记录每个岛屿的形状。
3. 将每个岛屿的形状存储到一个集合中,以避免重复。
4. 集合的大小即为不同岛屿的数量。
5. 使用Java Stream API来处理集合操作。
*/
 
import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;
 
public class NumberOfDistinctIslandsStream {
    public int numDistinctIslands(int[][] grid) {
        Set<String> uniqueIslands = new HashSet<>();
        int rows = grid.length;
        int cols = grid[0].length;
        boolean[][] visited = new boolean[rows][cols];
 
        IntStream.range(0, rows).forEach(i ->
            IntStream.range(0, cols).forEach(j -> {
                if (grid[i][j] == 1 && !visited[i][j]) {
                    StringBuilder shape = new StringBuilder();
                    dfs(grid, visited, i, j, shape, 'O');
                    uniqueIslands.add(shape.toString());
                }
            })
        );
 
        return uniqueIslands.size();
    }
 
    private void dfs(int[][] grid, boolean[][] visited, int i, int j, StringBuilder shape, char direction) {
        if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == 0 || visited[i][j]) {
            return;
        }
 
        visited[i][j] = true;
        shape.append(direction);
 
        dfs(grid, visited, i - 1, j, shape, 'U');
        dfs(grid, visited, i + 1, j, shape, 'D');
        dfs(grid, visited, i, j - 1, shape, 'L');
        dfs(grid, visited, i, j + 1, shape, 'R');
 
        shape.append('B');
    }
}

解释

方法:

这个题解的思路是先用 DFS 或 BFS 找出每个岛屿的形状,并将岛屿的形状表示为相对于左上角的坐标偏移量。然后对每个形状进行 8 种变换(旋转和翻转),将变换后的形状进行比较,去除重复的岛屿形状,最终得到不同岛屿的数量。

时间复杂度:

O(mn * log(mn))

空间复杂度:

O(mn)

代码细节讲解

🦆
题解中提到的每个岛屿形状进行8种变换的具体方法是什么,能否详细解释这些变换如何实现?
题解中的8种变换包括岛屿形状的旋转和翻转操作,具体包括:1) 原始形状;2) 顺时针旋转90度;3) 顺时针旋转180度;4) 顺时针旋转270度;5) 水平翻转;6) 垂直翻转;7) 主对角线翻转(即沿左上到右下的对角线翻转);8) 副对角线翻转(即沿右上到左下的对角线翻转)。实现这些变换的关键在于坐标的变换规则:例如,顺时针旋转90度将坐标(x, y)变换为(y, max_x - x),其中max_x是形状中x坐标的最大值。水平翻转则将每个点的y坐标映射到max_y - y,其中max_y是形状中y坐标的最大值。通过这样的坐标变换,可以生成岛屿的所有可能的变换形态。
🦆
如何确保在进行岛屿形状变换后,能够正确地比较并去除重复的岛屿形状?
为了确保能够正确比较并去除重复的岛屿形状,题解中采用了两个关键步骤:首先,对每种变换后的形状进行排序,以确保形状的坐标列表是有序的,这使得相同形状的不同表示(例如旋转或翻转后)能够被归一化到相同的序列。其次,使用集合(set)存储这些排序后的形状,利用集合的特性来自动去除重复项(因为集合不允许存储重复元素)。这样,只要有任一变换形态已存在于集合中,就不再添加新的变换形态,从而实现了重复形状的去除。
🦆
在将岛屿形状添加到集合前,为什么需要对形状的坐标进行排序?这样做的目的是什么?
对岛屿形状的坐标进行排序是为了确保所有变换后的形状可以有一个统一的表示方式,即无论形状如何旋转或翻转,其坐标序列在排序后都应相同。这样做的目的是消除由于坐标顺序不同而导致的重复存储问题。例如,不同的变换可能导致形状看起来不同,但实际上是同一个形状的不同表达。通过排序,可以将这些形状归一化,使得相同的形状具有相同的坐标序列,从而便于比较和去重。
🦆
为什么选择使用集合(set)来存储岛屿的形状,而不是使用列表(list)或其他数据结构?
使用集合(set)来存储岛屿形状是因为集合具有唯一性的特点,即它不允许存储重复的元素。这一特性非常适合用于去除重复的岛屿形状,确保每个形状只被计算一次。如果使用列表(list) 或其他数据结构,则需要手动管理重复数据的问题,这不仅增加了编程的复杂性,也可能导致效率低下。集合在内部通过哈希表实现,具有高效的查找、插入和删除操作,这使得它非常适合本题的需求。

相关问题

不同岛屿的数量

不同岛屿的数量