LeetCode Hot 100 | 图论(C++ 题解)
Hot100 图论:
200 / 994 / 207 / 208。
目录
一、200. Number of Islands(岛屿数量)🟡 中等
题目描述
给你一个由 '1'(陆地)和 '0'(水)组成的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水拦截,并且每座岛屿只能由水平方向和/或垂直方向上相邻的陆地连接而成。
示例 1:
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
示例 2:
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
图解

解题思路
DFS 染色(本解):
遍历网格,每遇到 '1' 就发起一次 DFS,将该岛屿的所有格子标记为 '2'(已访问),同时岛屿计数 +1。
DFS 的边界条件:越界、非 '1' 时返回。通过标记 '2' 避免重复访问。
举例(示例 2):
(0,0) 是 '1' → DFS 标记 (0,0),(0,1),(1,0),(1,1) 为 '2',ans=1
(2,2) 是 '1' → DFS 标记 (2,2),ans=2
(3,3) 是 '1' → DFS 标记 (3,3),(3,4),ans=3
最终:3 ✅
代码亮点: 使用 C++23 的 this auto && dfs 语法实现 lambda 递归,写法简洁。
复杂度: 时间 O(m×n),空间 O(m×n)(递归栈)
C++ 代码
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int rowSize = grid.size();
int colSize = grid[0].size();
int ans = 0;
auto dfs = [&] (this auto && dfs, int row, int col) -> void {
if (row < 0 || row >= rowSize || col < 0 || col >= colSize ||
grid[row][col] != '1')
return;
grid[row][col] = '2';
dfs(row, col - 1);
dfs(row, col + 1);
dfs(row + 1, col);
dfs(row - 1, col);
};
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize; j++) {
if (grid[i][j] == '1') {
dfs(i, j);
ans++;
}
}
}
return ans;
}
};
二、994. Rotting Oranges(腐烂的橘子)🟡 中等
题目描述
在给定的 m × n 网格 grid 中,每个单元格可以有以下三个值之一:
0代表空单元格1代表新鲜橘子2代表腐烂的橘子
每分钟,腐烂的橘子周围 4 个方向上相邻的新鲜橘子都会腐烂。
返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
示例 1:
输入:grid = [[2,1,1],[1,1,0],[0,1,1]]
输出:4
示例 2:
输入:grid = [[2,1,1],[0,1,1],[1,0,1]]
输出:-1(左下角的 1 无法被感染)
示例 3:
输入:grid = [[0,2]]
输出:0(没有新鲜橘子)
图解

解题思路
多源 BFS(本解):
同时从所有腐烂橘子出发做 BFS,每一轮 BFS 相当于过一分钟。
初始化:
- 统计所有新鲜橘子数量
fresh - 将所有腐烂橘子位置加入队列
BFS 过程:每轮弹出当前队列中的所有腐烂橘子,扩散到相邻新鲜橘子,将其标记为 2 并加入队列,fresh--,轮次 ans++。
终止:当 fresh == 0 或队列为空时停止。若结束后 fresh > 0 说明有橘子无法腐烂,返回 -1。
举例(示例 1):
初始:fresh=6,queue={(0,0)}
第1轮:(0,0)扩散→(0,1),(1,0)腐烂,fresh=4,ans=1
第2轮:(0,1),(1,0)扩散→(0,2),(1,1)腐烂,fresh=2,ans=2
第3轮:(0,2),(1,1)扩散→(2,1)腐烂,fresh=1,ans=3
第4轮:(2,1)扩散→(2,2)腐烂,fresh=0,ans=4
fresh==0,返回 4 ✅
复杂度: 时间 O(m×n),空间 O(m×n)
C++ 代码
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int ans = 0;
int rowSize = grid.size();
int colSize = grid[0].size();
queue<pair<int, int>> q;
int fresh = 0;
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize; j++) {
if (grid[i][j] == 1)
fresh++;
else if (grid[i][j] == 2)
q.push(make_pair(i, j));
}
}
vector<vector<int>> dir {{1, 0}, {-1, 0}, {0, -1}, {0, 1}};
while (fresh && !q.empty()) {
int size = q.size();
for (int j = 0; j < size; j++) {
auto pos = q.front();
q.pop();
for (int i = 0; i < dir.size(); i++) {
int x = pos.first + dir[i][0];
int y = pos.second + dir[i][1];
if (x >= 0 && x < rowSize && y >= 0 && y < colSize &&
grid[x][y] == 1) {
fresh--;
grid[x][y] = 2;
q.push(make_pair(x, y));
}
}
}
ans++;
}
return fresh ? -1 : ans;
}
};
三、207. Course Schedule(课程表)🟡 中等
题目描述
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1。
在选修某些课程之前需要一些先修课程。先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi],表示如果要学习课程 ai 则必须先学习课程 bi。
请你判断是否可能完成所有课程的学习?
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true(先上0,再上1)
示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false(循环依赖)
图解

解题思路
拓扑排序(BFS Kahn 算法)(本解):
如果课程间存在循环依赖,则无法完成所有课程,等价于判断有向图是否存在环。
用拓扑排序(BFS 版本):
- 建图:
umap[a]存 a 的前驱,inDegree[b]++(b 有入度) - 将所有入度为 0 的节点入队,计数
count - BFS:每次出队一个节点,将其所有前驱的入度 -1,若入度变为 0 则入队,
count++ - 若
count == numCourses,说明所有节点都被处理过,无环
注意: 代码中 umap[prerequisites[i][0]].push_back(prerequisites[i][1]) 即 a → b 方向(a 依赖 b,b 是 a 的先修),inDegree[b]++ 计的是 b 被依赖的次数(b 被解锁后才能减少依赖 b 的课程的入度)。
举例([[1,0],[0,1]],循环):
inDegree = [1, 1](0和1互相依赖)
没有入度为0的节点,count=0 ≠ 2
返回 false ✅
复杂度: 时间 O(V+E),空间 O(V+E)
C++ 代码
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
unordered_map<int, vector<int>> umap;
vector<int> inDegre(numCourses, 0);
int count = 0;
queue<int> q;
for (int i = 0; i < prerequisites.size(); i++) {
umap[prerequisites[i][0]].push_back(prerequisites[i][1]);
inDegre[prerequisites[i][1]]++;
}
for (int i =0; i < numCourses; i++) {
if (!inDegre[i]) {
q.push(i);
count++;
}
}
while (!q.empty()) {
int courses = q.front();
q.pop();
vector<int> cours = umap[courses];
for (auto cour : cours) {
inDegre[cour]--;
if (!inDegre[cour]){
q.push(cour);
count++;
}
}
}
return (count == numCourses);
}
};
四、208. Implement Trie (Prefix Tree)(实现 Trie 前缀树)🟡 中等
题目描述
Trie(发音类似 “try”)或者说前缀树是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
请你实现 Trie 类:
Trie():初始化前缀树对象void insert(String word):向前缀树中插入字符串wordboolean search(String word):如果word在前缀树中,返回trueboolean startsWith(String prefix):如果之前已插入的字符串中有以prefix为前缀的字符串,返回true
示例:
输入:
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出:[null, null, true, false, true, null, true]
图解

解题思路
哈希表 Trie 节点(本解):
每个节点 TreeNode 包含:
unordered_map<char, TreeNode*> umap:子节点映射bool isEnd:标记是否为某个单词的结尾
三个操作:
insert:从 root 出发,对每个字符,若不存在则新建节点,最后标记isEnd = truesearch:从 root 出发沿字符遍历,若中途找不到字符返回 false,走完后返回cur->isEndstartsWith:与 search 相同,但最后直接返回 true(不需要 isEnd)
举例(insert “apple” 后 search “app”):
insert "apple": root→a→p→p→l→e(isEnd=true)
search "app":
root→a→p→p → cur 存在
走完了,cur->isEnd = false(p 不是单词结尾)
返回 false ✅
startsWith "app":
root→a→p→p → 走完,返回 true ✅
复杂度: 时间 O(L)(L 为字符串长度),空间 O(总字符数)
C++ 代码
class Trie {
struct TreeNode {
unordered_map<char, TreeNode *> umap;
bool isEnd;
TreeNode() {
umap.clear();
isEnd = false;
}
};
public:
TreeNode *root;
Trie() {
root = new TreeNode();
}
void insert(string word) {
TreeNode *cur = root;
for (auto &c : word) {
if (!cur->umap.count(c)) {
cur->umap[c] = new TreeNode();
}
cur = cur->umap[c];
}
cur->isEnd = true;
}
bool search(string word) {
TreeNode *cur = root;
for (auto &c : word) {
if (!cur->umap.count(c))
return false;
cur = cur->umap[c];
}
return cur->isEnd;
}
bool startsWith(string prefix) {
TreeNode *cur = root;
for (auto &c : prefix) {
if (!cur->umap.count(c))
return false;
cur = cur->umap[c];
}
return true;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
总结
| 题号 | 题目 | 难度 | 核心思路 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|---|---|
| 200 | 岛屿数量 | 🟡 中等 | DFS 染色,标记 ‘2’ | O(m×n) | O(m×n) |
| 994 | 腐烂的橘子 | 🟡 中等 | 多源 BFS,逐轮扩散 | O(m×n) | O(m×n) |
| 207 | 课程表 | 🟡 中等 | 拓扑排序(Kahn BFS)判断有无环 | O(V+E) | O(V+E) |
| 208 | 实现 Trie | 🟡 中等 | 哈希表 Trie 节点,isEnd 标记词尾 | O(L) | O(总字符) |
如果这篇文章对你有帮助,欢迎点赞收藏 ⭐,也欢迎在评论区交流!
&spm=1001.2101.3001.5002&articleId=163363771&d=1&t=3&u=db24ed1cd00f444facf691aeda8d1cbc)
1231

被折叠的 条评论
为什么被折叠?



