岛屿数量
广度优先搜索
模板参考代码随想录 (programmercarl.com)
基本思想就是有两个矩阵,分别是图的矩阵graph和遍历过的矩阵visited,两者大小相等,但graph有原值,visited最开始全0,表示为踏足过,每次广度优先搜索会将一块岛屿全走一遍,即visited矩阵在graph的一块岛屿上的面积全部置1。
我们需对矩阵graph每个元素都遍历,在遍历的过程中,若有graph中元素数值为1(是一块岛屿或岛屿的一部分)且是我们之前未踏足过的新大陆(visited = 0),则将结果result + 1,且通过BFS(广度优先搜索)将这块岛屿在visited上标记出来,防止重复踏足。代码如下。
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// dir数组定义了四个方向,分别代表上、右、下、左的偏移量
int dir[4][2] = {0,1,1,0,-1,0,0,-1};
// 定义一个队列,用于存储待访问的节点(pair<int,int>类型表示坐标)
queue<pair<int,int>> Queue;
// bfs函数实现了广度优先搜索算法
// grid参数表示输入的二维数组(图)
// visited参数表示访问标记数组,用于标记已访问过的节点
// x,y参数表示开始搜索的节点的坐标
void bfs(vector<vector<int>>& grid,vector<vector<int>>& visited, int x, int y){
Queue.push({x,y}); // 将起始节点加入队列
visited[x][y] = 1; // 标记起始节点为已访问
while(!Queue.empty()){ // 当队列不为空时,继续搜索
pair<int,int> cur = Queue.front(); // 获取队列中的当前节点
Queue.pop(); // 将当前节点从队列中移除
int cur_x = cur.first; // 获取当前节点的x坐标
int cur_y = cur.second; // 获取当前节点的y坐标
for(int i = 0; i < 4; i++){ // 遍历四个方向
int nextx = cur_x + dir[i][0]; // 计算下一个节点的x坐标
int nexty = cur_y + dir[i][1]; // 计算下一个节点的y坐标
// 判断下一个节点是否越界
if(nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size())
continue; // 如果越界,则跳过当前方向
// 判断下一个节点是否未被访问且为有效节点(值为1)
if(!visited[nextx][nexty] && grid[nextx][nexty]){
Queue.push({nextx,nexty}); // 将下一个节点加入队列
visited[nextx][nexty] = 1; // 标记下一个节点为已访问
}
}
}
}
int main() {
int N,M; // N和M分别表示二维数组的行数和列数
int result = 0; // result用于存储连通区域的总数
cin>>N>>M; // 输入二维数组的行数和列数
// 初始化二维数组graph和访问标记数组visited
vector<vector<int>> graph(N,vector<int>(M,0));
vector<vector<int>> visited(N,vector<int>(M,0));
// 输入二维数组graph的值
for(int i = 0; i < N;i++){
for(int j = 0; j < M;j++){
cin>>graph[i][j];
}
}
// 遍历二维数组graph,对每个未访问过的节点执行bfs
for(int i = 0; i <N; i++){
for(int j = 0; j < M; j++){
if(!visited[i][j] && graph[i][j]){ // 如果节点未被访问且值为1
bfs(graph,visited,i,j); // 执行bfs
result++; // 连通区域数加1
}
}
}
cout<<result<<endl; // 输出连通区域的总数
return 0; // 程序结束
}
算法的时间复杂度为O(m*n),遍历了graph的每一个点,空间复杂度为O(m*n),维护一个visited数组。
深度优先搜索
利用递归来对graph中一个值为1的点四个方位的点进行递归查询,若碰到不符合条件(越界或visited数组值为1时,则自动返回),代码在整体和广度优先搜索差不多,除了使用的dfs而不是bfs,没有使用队列但使用了递归。
#include<iostream>
#include<vector>
using namespace std;
// dir数组定义了四个方向,分别代表上、右、下、左的偏移量
int dir[4][2] = {0,1,1,0,-1,0,0,-1};
// dfs函数实现了深度优先搜索算法
// grid参数表示输入的二维数组(图)
// visited参数表示访问标记数组,用于标记已访问过的节点
// x,y参数表示当前搜索的节点的坐标
void dfs(const vector<vector<int>>& grid,vector<vector<int>>& visited,int x, int y){
for(int i = 0; i < 4;i++){ // 遍历四个方向
int nextx = x + dir[i][0]; // 计算下一个节点的x坐标
int nexty = y + dir[i][1]; // 计算下一个节点的y坐标
// 判断下一个节点是否越界
if(nextx<0 || nexty<0 || nextx >= grid.size()||nexty >= grid[0].size())
continue; // 如果越界,则跳过当前方向
// 判断下一个节点是否未被访问且为有效节点(值为1)
if(!visited[nextx][nexty] && grid[nextx][nexty]) {
visited[nextx][nexty] = 1; // 标记下一个节点为已访问
dfs(grid, visited, nextx, nexty); // 递归搜索下一个节点
}
}
}
int main() {
int N,M; // N和M分别表示二维数组的行数和列数
int result = 0; // result用于存储连通区域的总数
cin>>N>>M; // 输入二维数组的行数和列数
// 初始化二维数组graph和访问标记数组visited
vector<vector<int>> graph(N,vector<int>(M,0));
vector<vector<int>> visited(N,vector<int>(M,0));
// 输入二维数组graph的值
for(int i = 0; i < N;i++){
for(int j = 0; j < M;j++){
cin>>graph[i][j];
}
}
// 遍历二维数组graph,对每个未访问过的节点执行dfs
for(int i = 0; i <N; i++){
for(int j = 0; j < M; j++){
if(!visited[i][j] && graph[i][j]){ // 如果节点未被访问且值为1
result++; // 连通区域数加1
dfs(graph,visited,i,j); // 执行dfs
}
}
}
cout<<result<<endl; // 输出连通区域的总数
return 0;
}
算法的时间复杂度和空间复杂度与上题相同,这里需要注意的是,递归调用栈的最大深度为N*M(每个节点都递归调用一次),所以空间复杂度整体上没有变化。
岛屿的最大面积
在每次广度搜索或者深度搜索中,会对visited数组进行置1,比较每次置1的数目,即可求得岛屿最大面积。
广度优先搜索
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// dir数组定义了四个方向,分别代表上、右、下、左的偏移量
int dir[4][2] = {0,1,1,0,-1,0,0,-1};
// 定义一个队列,用于存储待访问的节点(pair<int,int>类型表示坐标)
queue<pair<int,int>> Queue;
// bfs函数实现了广度优先搜索算法
// grid参数表示输入的二维数组(图)
// visited参数表示访问标记数组,用于标记已访问过的节点
// x,y参数表示开始搜索节点的下标
void bfs(vector<vector<int>>& grid,vector<vector<int>>& visited, int x, int y){
Queue.push({x,y}); // 将起始节点加入队列
visited[x][y] = 1; // 标记起始节点为已访问
while(!Queue.empty()){ // 当队列不为空时,继续搜索
pair<int,int> cur = Queue.front(); // 获取队列中的当前节点
Queue.pop(); // 将当前节点从队列中移除
int cur_x = cur.first; // 获取当前节点的x坐标
int cur_y = cur.second; // 获取当前节点的y坐标
for(int i = 0; i < 4; i++){ // 遍历四个方向
int nextx = cur_x + dir[i][0]; // 计算下一个节点的x坐标
int nexty = cur_y + dir[i][1]; // 计算下一个节点的y坐标
// 判断下一个节点是否越界
if(nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size())
continue; // 如果越界,则跳过当前方向
// 判断下一个节点是否未被访问且为有效节点(值为1)
if(!visited[nextx][nexty] && grid[nextx][nexty]){
Queue.push({nextx,nexty}); // 将下一个节点加入队列
visited[nextx][nexty] = 1; // 标记下一个节点为已访问
}
}
}
}
int main() {
int N,M; // N和M分别表示二维数组的行数和列数
int max_area = 0; // max_area用于存储最大连通区域的大小
int sumofvisitedlasted = 0; // sumofvisitedlasted用于存储上一次遍历的总访问节点数
int sumofvisited = 0; // sumofvisited用于存储当前遍历的总访问节点数
cin>>N>>M; // 输入二维数组的行数和列数
// 初始化二维数组graph和访问标记数组visited
vector<vector<int>> graph(N,vector<int>(M,0));
vector<vector<int>> visited(N,vector<int>(M,0));
// 输入二维数组graph的值
for(int i = 0; i < N;i++){
for(int j = 0; j < M;j++){
cin>>graph[i][j];
}
}
// 遍历二维数组graph,对每个未访问过的节点执行bfs
for(int i = 0; i <N; i++){
for(int j = 0; j < M; j++){
if(!visited[i][j] && graph[i][j]){ // 如果节点未被访问且值为1
bfs(graph,visited,i,j); // 执行bfs
for(int i = 0; i < N;i++){ // 计算当前遍历的总访问节点数
for(int j = 0 ; j < M; j++){
sumofvisited+=visited[i][j];
}
}
max_area = max(max_area,sumofvisited - sumofvisitedlasted); // 更新最大连通区域的大小
sumofvisitedlasted = sumofvisited; // 更新上一次遍历的总访问节点数
sumofvisited = 0; // 重置当前遍历的总访问节点数
}
}
}
cout<<max_area<<endl; // 输出最大连通
算法的时间复杂度为O(M*N),空间复杂度为O(M*N)。
深度优先搜索
#include <iostream>
#include <vector>
using namespace std;
// dir数组定义了四个方向,分别代表上、右、下、左的偏移量
int dir[4][2] = {0,1,1,0,-1,0,0,-1};
// dfs函数实现了深度优先搜索算法
// grid参数表示输入的二维数组(图)
// visited参数表示访问标记数组,用于标记已访问过的节点
// x,y参数表示当前搜索节点的下标
void dfs(const vector<vector<int>>& grid, vector<vector<int>>& visited,int x, int y){
for(int i = 0; i < 4; i ++){ // 遍历四个方向
int nextx = x + dir[i][0]; // 计算下一个节点的x坐标
int nexty = y + dir[i][1]; // 计算下一个节点的y坐标
// 判断下一个节点是否越界
if(nextx<0 or nexty < 0 or nextx>=grid.size() or nexty >= grid[0].size())
continue; // 如果越界,则跳过当前方向
// 判断下一个节点是否未被访问且为有效节点(值为1)
if(!visited[nextx][nexty] and grid[nextx][nexty]){
visited[nextx][nexty] = 1; // 标记下一个节点为已访问
dfs(grid,visited,nextx,nexty); // 递归搜索下一个节点
}
}
}
int main() {
int N,M; // N和M分别表示二维数组的行数和列数
int max_area = 0; // max_area用于存储最大连通区域的大小
int sumofvisitedlasted = 0; // sumofvisitedlasted用于存储上一次遍历的总访问节点数
int sumofvisited = 0; // sumofvisited用于存储当前遍历的总访问节点数
cin>>N>>M; // 输入二维数组的行数和列数
// 初始化二维数组graph和访问标记数组visited
vector<vector<int>> graph(N,vector<int>(M,0));
vector<vector<int>> visited(N,vector<int>(M,0));
// 输入二维数组graph的值
for(int i = 0; i < N;i++){
for(int j = 0; j < M;j++){
cin>>graph[i][j];
}
}
// 遍历二维数组graph,对每个未访问过的节点执行dfs
for(int i = 0; i <N; i++){
for(int j = 0; j < M; j++){
if(!visited[i][j] and graph[i][j]){ // 如果节点未被访问且值为1
visited[i][j] = 1; // 标记起始节点为已访问
dfs(graph,visited,i,j); // 执行dfs
for(int i = 0; i < N;i++){ // 计算当前遍历的总访问节点数
for(int j = 0 ; j < M; j++){
sumofvisited+=visited[i][j];
}
}
max_area = max(max_area,sumofvisited - sumofvisitedlasted); // 更新最大连通区域的大小
sumofvisitedlasted = sumofvisited; // 更新上一次遍历的总访问节点数
sumofvisited = 0; // 重置当前遍历的总访问节点数
}
}
}
cout<<max_area<<endl; // 输出最大连通区域的大小
return 0;
}
算法的时间复杂度和空间复杂度为O(N*M)。