算法训练营day51 图论② 岛屿数量深搜、广搜、最大面积
图论的第二篇博客,岛屿问题!主要了解两种最基本的遍历方法。同时在图论的部分,需要更加熟悉ACM模式的编程
岛屿数量深搜
本题思路,是用遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能遍历到的陆地都标记上。在遇到标记过的陆地节点和海洋节点的时候直接跳过。 这样计数器就是最终岛屿的数量。
所以深度搜索的部分在于把同一片陆地上的节点全部标记,这个是深度搜索在这道题中的作用
版本一
direction = [[0, 1], [1, 0], [0, -1], [-1, 0]] # 四个方向:上、右、下、左def dfs(grid, visited, x, y):"""对一块陆地进行深度优先遍历并标记"""for i, j in direction:next_x = x + inext_y = y + j# 下标越界,跳过if next_x < 0 or next_x >= len(grid) or next_y < 0 or next_y >= len(grid[0]):continue# 未访问的陆地,标记并调用深度优先搜索if not visited[next_x][next_y] and grid[next_x][next_y] == 1:visited[next_x][next_y] = Truedfs(grid, visited, next_x, next_y)if __name__ == '__main__': # 版本一n, m = map(int, input().split())# 邻接矩阵grid = []for i in range(n):grid.append(list(map(int, input().split())))# 访问表visited = [[False] * m for _ in range(n)]res = 0for i in range(n):for j in range(m):# 判断:如果当前节点是陆地,res+1并标记访问该节点,使用深度搜索标记相邻陆地。if grid[i][j] == 1 and not visited[i][j]:res += 1visited[i][j] = Truedfs(grid, visited, i, j)print(res)
版本二
和上一个版本区别在于递归调用结束判断的部分,这个细节需要对于深度搜索的熟练度和理解
direction = [[0, 1], [1, 0], [0, -1], [-1, 0]] # 四个方向:上、右、下、左def dfs(grid, visited, x, y):"""对一块陆地进行深度优先遍历并标记"""# 与版本一的差别,在调用前增加判断终止条件if visited[x][y] or grid[x][y] == 0:returnvisited[x][y] = Truefor i, j in direction:next_x = x + inext_y = y + j# 下标越界,跳过if next_x < 0 or next_x >= len(grid) or next_y < 0 or next_y >= len(grid[0]):continue# 由于判断条件放在了方法首部,此处直接调用dfs方法dfs(grid, visited, next_x, next_y)if __name__ == '__main__':# 版本二n, m = map(int, input().split())# 邻接矩阵grid = []for i in range(n):grid.append(list(map(int, input().split())))# 访问表visited = [[False] * m for _ in range(n)]res = 0for i in range(n):for j in range(m):# 判断:如果当前节点是陆地,res+1并标记访问该节点,使用深度搜索标记相邻陆地。if grid[i][j] == 1 and not visited[i][j]:res += 1dfs(grid, visited, i, j)print(res)
岛屿数量广搜
仍然是一样的计数思路
代码实现
这个地方和深度搜索中的区别在于:这里使用了队列,而深度搜索使用的是递归
from collections import deque
directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]
def bfs(grid, visited, x, y):que = deque([])que.append([x,y])visited[x][y] = Truewhile que:cur_x, cur_y = que.popleft()for i, j in directions:next_x = cur_x + inext_y = cur_y + jif next_y < 0 or next_x < 0 or next_x >= len(grid) or next_y >= len(grid[0]):continueif not visited[next_x][next_y] and grid[next_x][next_y] == 1: visited[next_x][next_y] = Trueque.append([next_x, next_y])def main():n, m = map(int, input().split())grid = []for i in range(n):grid.append(list(map(int, input().split())))visited = [[False] * m for _ in range(n)]res = 0for i in range(n):for j in range(m):if grid[i][j] == 1 and not visited[i][j]:res += 1bfs(grid, visited, i, j)print(res)if __name__ == "__main__":main()
超时写法
没有在首次入队列的时候标记陆地,导致每个陆地都会进入到队列中,如果标记了,会减少很多节点的入队列,来提升效率
需要注意的是,其实这里的超时问题和深度搜索中的结束判断的内在逻辑是一样的,是由搜索逻辑仅处理后续节点 还是 处理本节点以及后续节点(全部)的选择
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {queue<pair<int, int>> que;que.push({x, y});while(!que.empty()) {pair<int ,int> cur = que.front(); que.pop();int curx = cur.first;int cury = cur.second;visited[curx][cury] = true; // 从队列中取出在标记走过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(深度搜索)
# 四个方向
position = [[0, 1], [1, 0], [0, -1], [-1, 0]]
count = 0def dfs(grid, visited, x, y):"""深度优先搜索,对一整块陆地进行标记"""global count # 定义全局变量,便于传递count值for i, j in position:cur_x = x + icur_y = y + j# 下标越界,跳过if cur_x < 0 or cur_x >= len(grid) or cur_y < 0 or cur_y >= len(grid[0]):continueif not visited[cur_x][cur_y] and grid[cur_x][cur_y] == 1:visited[cur_x][cur_y] = Truecount += 1dfs(grid, visited, cur_x, cur_y)n, m = map(int, input().split())
# 邻接矩阵
grid = []
for i in range(n):grid.append(list(map(int, input().split())))
# 访问表
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]:count = 1visited[i][j] = Truedfs(grid, visited, i, j)result = max(count, result)print(result)
BFS(广度搜索)
from collections import dequeposition = [[0, 1], [1, 0], [0, -1], [-1, 0]] # 四个方向
count = 0def bfs(grid, visited, x, y):"""广度优先搜索对陆地进行标记"""global count # 声明全局变量que = deque()que.append([x, y])while que:cur_x, cur_y = que.popleft()for i, j in position:next_x = cur_x + inext_y = cur_y + j# 下标越界,跳过if next_x < 0 or next_x >= len(grid) or next_y < 0 or next_y >= len(grid[0]):continueif grid[next_x][next_y] == 1 and not visited[next_x][next_y]:visited[next_x][next_y] = Truecount += 1que.append([next_x, next_y])n, m = map(int, input().split())
# 邻接矩阵
grid = []
for i in range(n):grid.append(list(map(int, input().split())))
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]:count = 1visited[i][j] = Truebfs(grid, visited, i, j)res = max(result, count)print(result)