深度与广度优先搜索
深度优先遍历 DFS
- DFS 一般用来解决图的连通性问题
- 图的表示方法:邻接表,邻接矩阵
堆栈解法
function dfs(root) {
const stack = [];
const marked = {};
stack.push(root);
marked[root] = true;
while(stack.length) {
let v = stack.pop();
visit(v);
for(let u = 0; u < adj[v].length; u++) {
if (!marked[u]) {
stack.push(u);
marked[u] = true;
}
}
}
}
递归解法
const marked = {};
function dfs(root) {
visit(root);
marked[root] = true;
for(let u = 0; u < adj[root].length; u++) {
if (!marked[u]) {
dfs(u);
}
}
}
复杂度分析
- 邻接表(顶点 V,边 E): 时间复杂度 O(V + E)
- 邻接矩阵(顶点 V,边 E): 时间复杂度 O(V^2)
迷宫的最短路径
/**
* 迷宫 maze[][]
* 起点 A[x1, y1] --> 终点 B[x2, y2]
*/
function solve(maze) {
// 1. 除 A 意外的所有 0 都填充为 MAX_NUMBER
for (let i = 0; i < maze.length; i++) {
for (let j = 0; j < maze[0].length; j++) {
if(maze[i][j] === 0 && ! (i === A[0] && j === A[1])) {
maze[i][j] === Infinity;
}
}
}
// 2. DFS 的结果为,maze 的每格被填充为离终点最近的步数
dfs(maze, A[0], A[1]);
// 3. 判断是否抵达
if (maze[B[0]][B[1]] < Infinity) {
console.log('找到');
} else {
console.log('没找到');
}
}
function dfs(maze, x, y) {
// 1. 判断是否到达目的地
if (x === B[0] && y === B[1]) {
return;
}
// 2. 从四个方向进行尝试
for (let d = 0; d < 4; d++) {
let i = x + dx[d], j = (y + dy[d];
// 3. 判断下一点的步数是否大于当前步数 + 1
if (isSafe(maze, i, j) && maze[i][j] > maze[x][y] + 1) {
// 4. 若大于,则更新最较小的值,并继续 DFS
maze[i][j] = maze[x][y] + 1;
dfs(maze, i, j)
}
}
}
广度优先遍历 BFS
- BFS 一般用来解决最短路径问题
- 从起点出发,一层一层的进行
- 双端 BFS
- 同时从起点和终点开始进行广度优先遍历
- 例如,社交网络判断两人最少经过多少朋友才能相互认识
function bfs(root) {
const queue = [];
const marked = {};
queue.push(root);
marked[root] = true;
while(queue.length) {
let v = stack.shift();
visit(v);
for(let u = 0; u < adj[v].length; u++) {
if (!marked[u]) {
stack.push(u);
marked[u] = true;
}
}
}
}
复杂度分析
- 邻接表(顶点 V,边 E): 时间复杂度 O(V + E)
- 邻接矩阵(顶点 V,边 E): 时间复杂度 O(V^2)