四面都是水。
input:
grid = [
['1','1','1','1','0'],
['1','1','0','1','0'],
['1','1','0','0','0'],
['0','0','0','0','0']
]
output:1
void dfs(const vector<vector<char>>& grid, int x, int y)
如果需要保存搜索路径,可以增加一个 visited 数组,即 vector<vector<bool>> visited
终止条件达成,则返回。 或 无显性终止条件,不符合条件即不继续递归。
for (选择:本节点所连接的其他节点) {
处理节点;
dfs(图,选择的节点); // 递归
回溯,撤销处理结果
}
当遇到一个陆地 ‘1’ 时,我们可以通过深度优先搜索(DFS)将与该陆地相连的所有陆地都标记为已访问(例如,将它们改为 ‘0’)。这样,每次我们发现一个新的陆地 ‘1’ 时,就意味着我们找到了一个新的岛屿。
class Solution {
private:
void dfs(vector<vector<char>>& grid, int x, int y) {
int m = grid.size();
int n = grid[0].size();
grid[x][y] = '0'; // 标记为已访问
// 上下左右四个方向
if (x - 1 >= 0 && grid[x - 1][y] == '1') {
dfs(grid, x - 1, y);
}
if (x + 1 < m && grid[x + 1][y] == '1') {
dfs(grid, x + 1, y);
}
if (y - 1 >= 0 && grid[x][y - 1] == '1') {
dfs(grid, x, y - 1);
}
if (y + 1 < n && grid[x][y + 1] == '1') {
dfs(grid, x, y + 1);
}
}
public:
int numIslands(vector<vector<char>>& grid) {
int m = grid.size();
if (m == 0) return 0;
int n = grid[0].size();
int num_islands = 0;
for (int x = 0; x < m; ++x) {
for (int y = 0; y < n; ++y) {
if (grid[x][y] == '1'){
++num_islands;
dfs(grid, x, y);
}
}
}
return num_islands;
}
};
一般来说,BFS 适用于寻找最短路径的问题。
使用队列 queue<pair<int, int>> q 来保存当前层的节点:
使用栈 stack<pair<int, int>> s 来保存当前层的节点(不常用)。
queue<pair<int, int>> q;
q.push({start_x, start_y});
while (!q.empty()) {
// 处理当前节点:从队列中取出节点
auto [x, y] = q.front();
q.pop();
// 处理节点逻辑:上下左右,等等
for (选择:本节点所连接的其他节点) {
处理节点;
if (节点未被访问)
q.push(选择的节点); // 将相邻节点入队
}
}
BFS 在本题中不需要拿出节点进行处理,而是直接将节点的相邻节点入队,并将相邻节点标记为已访问。 如果遇到一个陆地 ‘1’,就将其相邻的陆地全部入队,并标记为已访问,直到队列为空,表示该岛屿的所有陆地都被访问过了。
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int m = grid.size();
if(!m) return 0;
int n = grid[0].size();
int num_islands = 0;
for(int x = 0; x < m; ++x) {
for(int y = 0; y < n; ++y) {
if(grid[x][y] == '1') {
++num_islands;
grid[x][y] = '0'; // 标记为已访问
queue<pair<int, int>> q;
q.push({x, y});
while(!q.empty()){
auto [curr_x, curr_y] = q.front();
q.pop();
// 上下左右四个方向
if (curr_x - 1 >= 0 && grid[curr_x - 1][curr_y] == '1') {
grid[curr_x - 1][curr_y] = '0';
q.push({curr_x - 1, curr_y});
}
if (curr_x + 1 < m && grid[curr_x + 1][curr_y] == '1') {
grid[curr_x + 1][curr_y] = '0';
q.push({curr_x + 1, curr_y});
}
if (curr_y - 1 >= 0 && grid[curr_x][curr_y - 1] == '1') {
grid[curr_x][curr_y - 1] = '0';
q.push({curr_x, curr_y - 1});
}
if (curr_y + 1 < n && grid[curr_x][curr_y + 1] == '1') {
grid[curr_x][curr_y + 1] = '0';
q.push({curr_x, curr_y + 1});
}
}
}
}
}
return num_islands;
}
};
在给定的网格中,每个单元格可以有以下三个值之一:
每分钟,任何与腐烂的橘子(在 4 个正方向上相邻)相邻的新鲜橘子都会腐烂。 返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
在这里使用BFS,因为如题目描述,这是一个每分钟逐层扩展的情况。
腐烂橘子同时影响相邻新鲜橘子,初始化时需要将所有腐烂橘子入队。 每分钟处理一层队列,确保同时腐烂的效果。使用
bool rotten标记当前分钟是否有新鲜橘子被腐烂,以决定是否增加分钟数。(和cnt计算岛屿大小的区别:岛屿大小需要每次都计算,而分钟数只在有变化时增加)
为什么不需要使用visited数组?因为可以直接修改原始网格。
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int min = 0, fresh = 0;
queue<pair<int, int>> q;
for(int i = 0; i < grid.size(); i++) {
for(int j = 0; j < grid[0].size(); j++)
if(grid[i][j] == 1) fresh++;
else if(grid[i][j] == 2) q.push({i, j});
}
vector<pair<int, int>> dirs = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
while(!q.empty()) {
int n = q.size();
bool rotten = false;
for(int i = 0; i < n; i++) {
auto x = q.front();
q.pop();
for(auto cur: dirs) {
int i = x.first + cur.first;
int j = x.second + cur.second;
if(i >= 0 && i < grid.size() && j >= 0 && j < grid[0].size() && grid[i][j] == 1) {
grid[i][j] = 2;
q.push({i, j});
fresh--;
rotten = true;
}
}
}
if(rotten) min++;
}
return fresh ? -1 : min;
}
};
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses- 1 。在选修某些课程之前需要一些先修课程。
例如,要想学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配先修课程的数组 prerequisites 来表示它们之间的先修关系,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true;否则返回 false 。