携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
深度优先搜索DFS和广度优先搜索BFS,主要用于对树或图的遍历
深度优先搜索DFS
DFS解决的是连通性问题,给定起点和终点,判断是否有一条路径能从起点走到终点,或者是从起始状态到最终状态。连通的路径可以有很多条,只需要找出一条即可
算法的思想:
- 从起点出发,选择一个可选方向不断往前走,直到无法继续走为止
- 然后尝试另一个方向,直到走到终点,上下左右一路走到黑
- 类似于走迷宫一样,不断往里走,所以称为深度优先
DFS的实现方式:
- 递归实现
- 递归实现DFS代码看上去很简洁
- 但是压入和弹出系统栈会需要较多时间,压栈很深会造成效率低下
- 非递归实现
- 深度优先遍历的非递归实现必须依赖栈结构
- 栈的特点是先进后出
广度优先搜索BFS
BFS解决的是最短路径问题和层序遍历
算法的思想:
- 从起点出发,一层一层进行,每层中的点距离起始点的步数都相同
- 同时从起点和终点出发的广度优先搜索被称为双端BFS,双端BFS可以大大提高效率
BFS的实现方式:
- 广度优先遍历的非递归实现必须依赖队列结构
- 队列的特点是先进先出
例题
需要标记visited的节点,避免被重复遍历或有环时死循环
岛屿的最大面积
DFS解法
var maxAreaOfIsland = function(grid) {
if (!grid.length) {
return 0
}
let m = grid.length
let n = grid[0].length
function dfs (x, y) {
// 判断是否越界
if (x < 0 || x > m-1 || y < 0 || y > n-1) {
return 0
}
// 判断是否岛屿,不是岛屿则返回,是岛屿则标记访问过,置0
if (grid[x][y] === 0) {
return 0
}
grid[x][y] = 0
let num = 1
// 设置方向数组
let dx = [-1, 1, 0, 0], dy = [0, 0, -1, 1]
for (let i = 0; i < dx.length; i++) {
// 上下左右四个方向递归dfs
num += dfs(x + dx[i], y + dy[i])
}
return num
}
let res = 0
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
res = Math.max(res, dfs(i, j))
}
}
return res
};
时间复杂度:O(mn),空间复杂度:O(mn)
BFS解法
var maxAreaOfIsland = function(grid) {
if (!grid.length) {
return 0
}
let m = grid.length
let n = grid[0].length
let res = 0
let dx = [-1, 1, 0, 0], dy = [0, 0, -1, 1]
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (grid[i][j] === 0) {
continue
}
let num = 0
let queen = [[i, j]]
while(queen.length) {
let [x,y] = queen.shift()
// 判断是否越界
if (x < 0 || x > m-1 || y < 0 || y > n-1) {
continue
}
// 判断是否岛屿,不是岛屿则返回,是岛屿则标记访问过,置0
if (grid[x][y] === 0) {
continue
}
grid[x][y] = 0
num++
for (let h = 0; h < dx.length; h++) {
queen.push([x+dx[h], y+dy[h]])
}
}
res = Math.max(res, num)
}
}
return res
};
时间复杂度:O(mn),空间复杂度:O(mn)