目录标题
- 1.岛屿数量⭐
- 2.腐烂的橘子
-
- [(1)多源 BFS](#(1)多源 BFS)
- 3.课程表
-
- [(1)有向图 + 拓扑排序 + 判断是否有环](#(1)有向图 + 拓扑排序 + 判断是否有环)
- [4.实现 Trie (前缀树)](#4.实现 Trie (前缀树))
1.岛屿数量⭐
(1)DFS
把每一个 '1' 看成图中的一个节点。如果两个 '1' 上下左右相邻,那么它们之间就有一条边。
遍历整个二维数组
- 如果当前位置是 '0', 跳过
- 如果当前位置是 '1', 说明发现了一个新的岛屿 count++
- 然后使用 DFS,把和当前岛屿连在一起的所有 '1', 全部标记掉
java
int res = 0;
public int numIslands(char[][] grid) {
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1') {
res++;
dfs(grid, i, j);
}
}
}
return res;
}
public void dfs(char[][] grid, int i, int j) {
//越界或者已经是水了
if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
- 每个位置都需要处理一次,时间复杂度:O(m × n)
- 最坏情况下整个地图都是陆地,递归栈:空间复杂度:O(m × n)
(2)BFS
java
public int numIslands(char[][] grid) {
int m = grid.length;
int n = grid[0].length;
int count = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
// 发现一个新的岛屿
if (grid[i][j] == '1') {
count++;
bfs(grid, i, j);
}
}
}
return count;
}
private void bfs(char[][] grid, int i, int j) {
int m = grid.length;
int n = grid[0].length;
Queue<int[]> queue = new ArrayDeque<>();
// 起点入队
queue.offer(new int[]{i, j});
// 标记为已经访问
grid[i][j] = '0';
// 四个方向:上、下、左、右
int[][] directions = {
{-1, 0},
{1, 0},
{0, -1},
{0, 1}
};
while (!queue.isEmpty()) {
int[] cur = queue.poll();
int x = cur[0];
int y = cur[1];
// 遍历四个方向
for (int[] direction : directions) {
int newX = x + direction[0];
int newY = y + direction[1];
// 不越界,并且是陆地
if (newX >= 0 && newX < m
&& newY >= 0 && newY < n
&& grid[newX][newY] == '1') {
// 标记已经访问
grid[newX][newY] = '0';
// 加入队列
queue.offer(new int[]{newX, newY});
}
}
}
}
2.腐烂的橘子
(1)多源 BFS
所有初始腐烂橘子同时加入队列
首先遍历整个 grid
- 遇到 1 fresh++ ;遇到 2: 加入 queue 然后 BFS。
- 每一轮处理当前队列中的所有橘子:
- while 队列不为空,表示当前这一层有 size 个腐烂橘子
- 遍历这 size 个橘子,检查上下左右, 如果邻居是新鲜橘子 1 就变成腐烂 2,fresh--,加入队列
- 如果这一轮真的腐烂了新橘子 minutes++
- 最后如果 fresh == 0,返回 minutes。否则返回 -1
java
public int orangesRotting(int[][] grid) {
Queue<int[]> queue = new LinkedList<>();
int n = grid.length;
int m = grid[0].length;
int fresh = 0;
int minutes = 0;
//先将所有腐烂的橘子加入队列中
//并统计新鲜的橘子数量
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 2) {
queue.offer(new int[]{i, j});
}
else if (grid[i][j] == 1) {
fresh++;
}
}
}
// 四个方向
int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
//开始bfs
//每次处理一轮队列中的烂橘子
//同时减少新鲜的橘子数量
while (!queue.isEmpty() && fresh>0) {
int size = queue.size();
//当前一层的处理
for (int i = 0; i < size; i++) {
int[] cur = queue.poll();
//将当前烂橘子的上下左右四个方向的橘子加入队列中
//并减少新鲜的橘子数量
for (int[] dir : directions) {
int nx = cur[0] + dir[0];
int ny = cur[1] + dir[1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
if (grid[nx][ny] != 1) {
continue;
}
fresh--;
grid[nx][ny] = 2;
queue.offer(new int[]{nx, ny});
}
}
}
minutes++;
}
return fresh==0?minutes:-1;
}
3.课程表
(1)有向图 + 拓扑排序 + 判断是否有环
- 入度 indegree 表示还有多少门前置课程没有学完。
- 如下例所示,课程 1 有两个前置课程。所以indegree1 = 2
- 首先找所有入度 = 0的课程。这些课程没有任何前置课程,可以直接学。
- 入度 = 0的课程全部加入队列,然后开始BFS
java
0 -> 1
2 -> 1
- BFS过程
- 首先取出一门当前可以学习的课程,把它学掉
- 它的所有后续课程入度 -1
- 如果某门课入度变成0说明它现在也可以学习了加入队列
- 最后如果学完数量 == numCourses,表示不存在环可以学完


java
public static boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> graph = new ArrayList<>();
//简历邻接图
for(int i=0;i<numCourses;i++) {
graph.add(new ArrayList<>());
}
// 计算入度
int[] indegree = new int[numCourses];
for(int[] edge : prerequisites) {
//edge[x][1]->edge[x][0]
graph.get(edge[1]).add(edge[0]);
indegree[edge[0]]++;
}
// 入度为0的点入队
Queue<Integer> queue = new ArrayDeque<>();
for(int i = 0; i < numCourses; i++) {
if(indegree[i] == 0) {
queue.offer(i);
}
}
// BFS拓扑排序
// 如果有一个环,那么入度就会减到负数,这时队列为空
while(!queue.isEmpty()) {
int course = queue.poll();
numCourses--;
//所有连接的节点入度减一
for(int i : graph.get(course)) {
indegree[i]--;
if(indegree[i] == 0) {
queue.offer(i);
}
}
}
return numCourses == 0;
}
4.实现 Trie (前缀树)
(1)字符树
用一棵"字符树"来存字符串,公共前缀只存一份

java
public class Trie {
// 字典树节点
private class TrieNode {
private boolean isEnd;
private TrieNode[] children = new TrieNode[26];
}
private TrieNode root;
// 构造函数典树
public Trie() {
root = new TrieNode();
}
// 插入单词
public void insert(String word) {
TrieNode cur = root;
for(char c : word.toCharArray()) {
int i = c - 'a';
if(cur.children[i] == null) {
cur.children[i] = new TrieNode();
}
cur = cur.children[i];
}
cur.isEnd = true;
}
// 搜索单词
public boolean search(String word) {
TrieNode cur = root;
for(char c : word.toCharArray()) {
int i = c - 'a';
if(cur.children[i] == null) {
return false;
}
cur = cur.children[i];
}
return cur.isEnd;
}
// 搜索前缀
public boolean startsWith(String prefix) {
TrieNode cur = root;
for(char c : prefix.toCharArray()) {
int i = c - 'a';
if(cur.children[i] == null) {
return false;
}
cur = cur.children[i];
}
return true;
}
}