题目描述:
给定一个由 1(陆地)和 0(水)组成的矩阵,你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。
输入描述:
第一行包含两个整数 N, M,表示矩阵的行数和列数。
后续 N 行,每行包含 M 个数字,数字为 1 或者 0。
输出描述:
输出一个整数,表示岛屿的数量。如果不存在岛屿,则输出 0。
输入示例:
根据测试案例中所展示,岛屿数量共有 3 个,所以输出 3。
思路
深度优先搜索
注意题目中每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
也就是说斜角度链接是不算了, 例如示例二,是三个岛屿,如图:
考虑遇到一个没有访问过的节点陆地,计数器就加1,然后把该节点陆地所能遍历到的陆地都标记上。
再遇到标记过的陆地节点和海洋节点的时候直接跳过。这样就得到了岛屿的最终数量。
广度优先搜索
这里有一个广搜中很重要的细节:
根本原因是只要 加入队列就代表 走过,就需要标记,而不是从队列拿出来的时候再去标记走过。
如果从队列拿出节点,再去标记这个节点走过,就会发生下图所示的结果,会导致很多节点重复加入队列。
代码
C++深度优先搜索(DFS这里只起到标记位置的作用,一次找出整个岛)
#include <iostream>
#include <vector>
using namespace std;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
void dfs(const vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
for (int i = 0; i < 4; ++i) {
int nextx = x + dir[i][0];
int nexty = y + dir[i][1];
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) {
continue; // 越界了,直接跳过
}
if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) {
// 没有访问过,同时是陆地
visited[nextx][nexty] = true;
dfs(grid, visited, nextx, nexty);
}
}
}
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m, 0));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> grid[i][j];
}
}
vector<vector<bool>> visited(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (!visited[i][j] && grid[i][j] == 1) {
visited[i][j] = true;
result++;
dfs(grid, visited, i, j); // 将与其链接的岛屿都标记上true
}
}
}
cout << result << endl;
}
C++广度优先搜索(把同一个岛屿的遍历在一个BFS内完成)
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
void bfs(const vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
queue<pair<int, int>> que;
que.push({x, y});
visited[x][y] = true;
while (!que.empty()) {
pair<int, int> cur = que.front(); que.pop();
int curx = cur.first;
int cury = cur.second;
for (int i = 0; i < 4; ++i) {
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1];
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) {
que.push({nextx, nexty}); // 压进队列,之后再检查。而不是像DFS一样,一路走到底。(没有BFS的套用)
visited[nextx][nexty] = true; // 只要压进队列立刻标记
}
}
}
}
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m, 0));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> grid[i][j];
}
}
vector<vector<bool>> visited(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (!visited[i][j] && grid[i][j] == 1) {
visited[i][j] = true;
result++; // 结果再进入BFS之前累加,因为找到了新岛屿
bfs(grid, visited, i, j); // 将与其链接的岛屿都标记上true
}
}
}
cout << result << endl;
}
Python深度优先搜索
from collections import deque
dir = [(0, 1), (1, 0), (0, -1), (-1, 0)]
def dfs(grid, visited, x, y):
for dx, dy in dir:
nextx, nexty = x + dx, y + dy
if nextx < 0 or nextx >= len(grid) or nexty < 0 or nexty >= len(grid[0]):
continue
if grid[nextx][nexty] == 1 and not visited[nextx][nexty]:
visited[nextx][nexty] = True
dfs(grid, visited, nextx, nexty)
def main():
n, m = map(int, input().split())
grid = [list(map(int, input().split())) for _ in range(n)] # 'map'函数返回的是一个迭代器,而不是一个列表。为了将其转换为列表,我们使用 list() 函数将迭代器转化为一个具体的列表。
visited = [[False] * (m) for _ in range(n)]
result = 0
for i in range(n):
for j in range(m):
if (grid[i][j] == 1 and not visited[i][j]):
result += 1
visited[i][j] = True
dfs(grid, visited, i, j)
print(result)
if __name__ == "__main__":
main()
Python广度优先搜索
from collections import deque
dir = [(0, 1), (1, 0), (0, -1), (-1, 0)]
def bfs(grid, visited, x, y):
queue = deque([(x, y)])
visited[x][y] = True
while (queue):
curx, cury = queue.popleft()
for dx, dy in dir:
nextx, nexty = curx + dx, cury + dy
if nextx < 0 or nextx >= len(grid) or nexty < 0 or nexty >= len(grid[0]): # 注意这里从0开始所以比n,m小1,如果等于的时候实际上已经越界
continue
if not visited[nextx][nexty] and grid[nextx][nexty] == 1:
queue.append((nextx, nexty))
visited[nextx][nexty] = True
def main():
n, m = map(int, input().split())
grid = [list(map(int, input().split())) for _ in range(n)] # 'map'函数返回的是一个迭代器,而不是一个列表。为了将其转换为列表,我们使用 list() 函数将迭代器转化为一个具体的列表。
visited = [[False] * (m) for _ in range(n)]
result = 0
for i in range(n):
for j in range(m):
if (grid[i][j] == 1 and not visited[i][j]):
result += 1
visited[i][j] = True
bfs(grid, visited, i, j)
print(result)
if __name__ == "__main__":
main()
Leetcode 200. Number of Islands
C++广度优先搜索 (字符用单引号,字符串用双引号)
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
class Solution {
public:
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
void bfs(const vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
queue<pair<int, int>> que;
que.push({x, y});
visited[x][y] = true;
while (!que.empty()) {
pair<int, int> cur = que.front(); que.pop();
int curx = cur.first;
int cury = cur.second;
for (int i = 0; i < 4; ++i) {
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1];
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') {
que.push({nextx, nexty}); // 压进队列,之后再检查。而不是像DFS一样,一路走到底。(没有BFS的套用)
visited[nextx][nexty] = true; // 只要压进队列立刻标记
}
}
}
}
int numIslands(vector<vector<char>>& grid) {
int n = grid.size();
int m = grid[0].size();
vector<vector<bool>> visited(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (!visited[i][j] && grid[i][j] == '1') {
visited[i][j] = true;
result++; // 结果再进入BFS之前累加,因为找到了新岛屿
bfs(grid, visited, i, j); // 将与其链接的岛屿都标记上true
}
}
}
return result;
}
};
Python深度优先搜索
class Solution:
dir = [(0, 1), (1, 0), (0, -1), (-1, 0)]
def dfs(self, grid, visited, x, y):
for dx, dy in self.dir:
nextx, nexty = x + dx, y + dy
if nextx < 0 or nextx >= len(grid) or nexty < 0 or nexty >= len(grid[0]):
continue
if grid[nextx][nexty] == '1' and not visited[nextx][nexty]:
visited[nextx][nexty] = True
self.dfs(grid, visited, nextx, nexty)
def numIslands(self, grid: List[List[str]]) -> int:
n, m = len(grid), len(grid[0])
visited = [[False] * (m) for _ in range(n)]
result = 0
for i in range(n):
for j in range(m):
if (grid[i][j] == '1' and not visited[i][j]):
result += 1
visited[i][j] = True
self.dfs(grid, visited, i, j)
return result