public class Main {
static int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
public static int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) return 0;
int m = grid.length, n = grid[0].length, count = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') {
dfs(grid, i, j, m, n);
count++;
}
}
}
return count;
}
static void dfs(char[][] grid, int i, int j, int m, int n) {
if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] != '1') return;
grid[i][j] = '0'; // 沉没陆地,标记已访问
for (int[] d : dirs) {
dfs(grid, i + d[0], j + d[1], m, n);
}
}
public static void main(String[] args) {
char[][] grid = {
{'1','1','0','0','0'},
{'1','1','0','0','0'},
{'0','0','1','0','0'},
{'0','0','0','1','1'}
};
System.out.println(numIslands(grid)); // 3
}
}
变体:如果网格巨大(10^6×10^6),递归 DFS 会栈溢出。改用迭代+BFS:
import java.util.*;
public class Main {
static int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
public static int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) return 0;
int m = grid.length, n = grid[0].length, count = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') {
bfs(grid, i, j, m, n);
count++;
}
}
}
return count;
}
static void bfs(char[][] grid, int i, int j, int m, int n) {
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{i, j});
grid[i][j] = '0';
while (!queue.isEmpty()) {
int[] curr = queue.poll();
for (int[] d : dirs) {
int ni = curr[0] + d[0], nj = curr[1] + d[1];
if (ni >= 0 && ni < m && nj >= 0 && nj < n && grid[ni][nj] == '1') {
grid[ni][nj] = '0';
queue.offer(new int[]{ni, nj});
}
}
}
}
public static void main(String[] args) {
char[][] grid = {
{'1','1','0'},
{'1','0','0'},
{'0','0','1'}
};
System.out.println(numIslands(grid)); // 2
}
}
讲清楚要点
读题是第一要务:关键词决定模式。本题「频率」→哈希表,「前 K」→堆。
复杂度预判:n=10^5 时,O(n log n) 和 O(n log k) 都能过,但后者更优。
堆的选择:前 K 大用小顶堆(弹最小),前 K 小用大顶堆(弹最大)。
代码结构:分步注释,每步对应一个算法步骤,便于调试。
你可能会懵的地方
为什么用小顶堆不是大顶堆?
小顶堆的堆顶是「前 K 大中最小的」,新元素比它大就替换,这样堆里始终保持前 K 大。