深度优先搜索(DFS)与广度优先搜索(BFS)全面解析 + 经典算法题实战(Java实现)
一、基本概念
DFS(Depth First Search)深度优先搜索
- 思想:沿着一条路径不断深入,直到无法继续,再回溯到上一个节点,尝试其他路径
- 实现方式:递归或显式栈
- 特点:
- 先深入,再回退
- 适用于需要穷尽所有路径或组合的场景
BFS(Breadth First Search)广度优先搜索
- 思想:从起点出发,先访问所有邻居节点,再依次访问邻居的邻居
- 实现方式:队列(Queue)
- 特点:
- 分层遍历
- 通常用于寻找最短路径
二、DFS 与 BFS 对比
特性 | DFS | BFS |
---|---|---|
数据结构 | 栈(递归调用栈或显式栈) | 队列(Queue) |
遍历方式 | 深度优先,先走到最深再回退 | 广度优先,逐层扩展 |
时间复杂度 | O(V+E) | O(V+E) |
空间复杂度 | O(h),h 为递归深度 | O(w),w 为最宽层的节点数 |
路径特性 | 不保证最短路径 | 能找到最短路径(无权图) |
应用场景 | 全排列、连通块、拓扑排序等 | 最短路径、层次遍历、最小步数等 |
三、可视化对比示例
以图为例(起点A):
A
/ \
B C
/ \ \
D E F
- DFS 遍历顺序:A → B → D → E → C → F
- BFS 遍历顺序:A → B → C → D → E → F
四、经典算法题示例
示例1:相同的树(LeetCode 100)
题目描述:
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的
示例 1:
输入:p = [1,2,3], q = [1,2,3]
输出:true
示例 2:
输入:p = [1,2], q = [1,null,2]
输出:false
示例 3:
输入:p = [1,2,1], q = [1,1,2]
输出:false
提示:
- 两棵树上的节点数目都在范围
[0, 100]
内 -104 <= Node.val <= 104
Java代码(DFS实现):
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
if(p.val != q.val) {
return false;
}
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}
递归法:
- 终止条件:两节点均为空时返回
true
;一个为空另一个非空时返回false
- 值比较:当前节点值不同则直接返回
false
- 递归子问题:分别比较左子树和右子树是否相同,必须全部相同才返回
true
Java代码(BFS实现):
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(p);
queue.offer(q);
while(!queue.isEmpty()) {
TreeNode node1 = queue.poll();
TreeNode node2 = queue.poll();
if(node1 == null && node2 == null) {
continue;
}
if(node1 == null || node2 == null) {
return false;
}
if(node1.val != node2.val) {
return false;
}
queue.offer(node1.left);
queue.offer(node2.left);
queue.offer(node1.right);
queue.offer(node2.right);
}
return true;
}
}
迭代法:
- 队列初始化:将两棵树的根节点成对加入队列
- 循环处理:每次取出两个节点进行比较,若值不同或结构不一致则返回
false
- 子节点入队:将对应的左子节点和右子节点成对加入队列,确保后续比较正确
示例2:岛屿数量(DFS-LeetCode 200)
题目描述:
给定一个二维的地图,‘1’ 表示陆地,‘0’ 表示水域,计算岛屿数量(连通的1为一个岛屿)
示例 1:
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
示例 2:
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
Java代码(DFS实现):
public class Solution {
public int numIslands(char[][] grid) {
int count = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1') {
dfs(grid, i, j);
count++;
}
}
}
return count;
}
private void dfs(char[][] grid, int i, int j) {
if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
}
示例3:迷宫最短路径(BFS)
题目描述:
从起点走到终点,每次可以上下左右移动一步,求最短步数
Java代码(BFS实现):
import java.util.LinkedList;
import java.util.Queue;
public class MazeShortestPathBFS {
public int shortestPath(int[][] maze, int[] start, int[] end) {
int rows = maze.length, cols = maze[0].length;
int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
boolean[][] visited = new boolean[rows][cols];
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{start[0], start[1], 0});
visited[start[0]][start[1]] = true;
while (!queue.isEmpty()) {
int[] cur = queue.poll();
if (cur[0] == end[0] && cur[1] == end[1]) {
return cur[2];
}
for (int[] dir : dirs) {
int x = cur[0] + dir[0], y = cur[1] + dir[1];
if (x >= 0 && y >= 0 && x < rows && y < cols && maze[x][y] == 0 && !visited[x][y]) {
visited[x][y] = true;
queue.offer(new int[]{x, y, cur[2] + 1});
}
}
}
return -1; // 无法到达终点
}
}
五、应用场景总结
应用方向 | 推荐算法 |
---|---|
树的遍历 | DFS |
寻找最短路径 | BFS |
迷宫走法 | BFS |
连通区域统计 | DFS |
图中的路径查找 | DFS/BFS |
组合/排列/搜索树 | DFS |