您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# Java深度优先和广度优先遍历怎么使用
## 一、引言
在计算机科学中,图的遍历是解决许多问题的基本操作。深度优先搜索(DFS)和广度优先搜索(BFS)是两种最常用的图遍历算法,它们在不同的场景下各有优势。本文将详细介绍这两种算法在Java中的实现和应用,帮助开发者理解其原理并掌握实际使用方法。
## 二、基本概念
### 1. 图的表示方式
在Java中,图通常通过以下两种方式表示:
- **邻接矩阵**:二维数组表示顶点间的连接关系
- **邻接表**:使用`List<List<Integer>>`或`Map<Integer, List<Integer>>`结构
```java
// 邻接表示例
List<List<Integer>> adjList = new ArrayList<>();
adjList.add(Arrays.asList(1, 2)); // 顶点0的邻居
adjList.add(Arrays.asList(3)); // 顶点1的邻居
特性 | DFS | BFS |
---|---|---|
数据结构 | 栈(递归/显式栈) | 队列 |
访问顺序 | 深度优先 | 广度优先 |
空间复杂度 | O(h) h为树高 | O(w) w为最大宽度 |
典型应用 | 拓扑排序、连通性问题 | 最短路径、层级遍历 |
public void dfsRecursive(int node, boolean[] visited, List<List<Integer>> graph) {
visited[node] = true;
System.out.print(node + " ");
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
dfsRecursive(neighbor, visited, graph);
}
}
}
public void dfsIterative(int start, List<List<Integer>> graph) {
Stack<Integer> stack = new Stack<>();
boolean[] visited = new boolean[graph.size()];
stack.push(start);
visited[start] = true;
while (!stack.isEmpty()) {
int node = stack.pop();
System.out.print(node + " ");
// 注意邻接点逆序入栈以保证顺序
for (int i = graph.get(node).size() - 1; i >= 0; i--) {
int neighbor = graph.get(node).get(i);
if (!visited[neighbor]) {
stack.push(neighbor);
visited[neighbor] = true;
}
}
}
}
public void bfs(int start, List<List<Integer>> graph) {
Queue<Integer> queue = new LinkedList<>();
boolean[] visited = new boolean[graph.size()];
queue.offer(start);
visited[start] = true;
while (!queue.isEmpty()) {
int node = queue.poll();
System.out.print(node + " ");
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
queue.offer(neighbor);
visited[neighbor] = true;
}
}
}
}
public void bfsWithLevels(int start, List<List<Integer>> graph) {
Queue<Integer> queue = new LinkedList<>();
boolean[] visited = new boolean[graph.size()];
int level = 0;
queue.offer(start);
visited[start] = true;
while (!queue.isEmpty()) {
int size = queue.size();
System.out.print("Level " + level + ": ");
for (int i = 0; i < size; i++) {
int node = queue.poll();
System.out.print(node + " ");
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
queue.offer(neighbor);
visited[neighbor] = true;
}
}
}
level++;
System.out.println();
}
}
两种算法的时间复杂度均为O(V+E),其中V是顶点数,E是边数。但实际性能受以下因素影响:
DFS更适合:
BFS更适合:
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
currentLevel.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(currentLevel);
}
return result;
}
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] != '1') {
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);
}
解决方案: - 对于深度很大的图,使用显式栈替代递归 - 设置最大递归深度限制
解决方案:
public void traverseDisconnectedGraph(List<List<Integer>> graph) {
boolean[] visited = new boolean[graph.size()];
for (int i = 0; i < graph.size(); i++) {
if (!visited[i]) {
// 选择DFS或BFS
bfs(i, graph, visited);
}
}
}
DFS路径记录示例:
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
List<List<Integer>> result = new ArrayList<>();
dfs(0, graph, new ArrayList<>(Arrays.asList(0)), result);
return result;
}
private void dfs(int node, int[][] graph, List<Integer> path, List<List<Integer>> result) {
if (node == graph.length - 1) {
result.add(new ArrayList<>(path));
return;
}
for (int neighbor : graph[node]) {
path.add(neighbor);
dfs(neighbor, graph, path, result);
path.remove(path.size() - 1); // 回溯
}
}
适用于已知起点和终点的场景:
public int bidirectionalBFS(Node start, Node end) {
Set<Node> q1 = new HashSet<>();
Set<Node> q2 = new HashSet<>();
Set<Node> visited = new HashSet<>();
q1.add(start);
q2.add(end);
int steps = 0;
while (!q1.isEmpty() && !q2.isEmpty()) {
// 总是扩展较小的队列
if (q1.size() > q2.size()) {
Set<Node> temp = q1;
q1 = q2;
q2 = temp;
}
Set<Node> nextLevel = new HashSet<>();
for (Node node : q1) {
if (q2.contains(node)) return steps;
for (Node neighbor : node.neighbors) {
if (!visited.contains(neighbor)) {
nextLevel.add(neighbor);
}
}
}
steps++;
visited.addAll(q1);
q1 = nextLevel;
}
return -1; // 未找到路径
}
结合DFS空间效率和BFS完备性的混合算法:
public int iddfs(Node start, Node target) {
int depth = 0;
while (true) {
Set<Node> visited = new HashSet<>();
boolean found = depthLimitedDFS(start, target, depth, visited);
if (found) return depth;
depth++;
}
}
private boolean depthLimitedDFS(Node node, Node target, int depth, Set<Node> visited) {
if (node == target) return true;
if (depth <= 0) return false;
visited.add(node);
for (Node neighbor : node.neighbors) {
if (!visited.contains(neighbor)) {
if (depthLimitedDFS(neighbor, target, depth - 1, visited)) {
return true;
}
}
}
return false;
}
深度优先和广度优先遍历是图算法中最基础也最重要的两种技术。理解它们的核心差异和适用场景,能够帮助我们在解决实际问题时做出更合理的选择:
通过本文的代码示例和应用场景分析,开发者应该能够根据具体问题需求,灵活选择并实现合适的遍历算法。在实际开发中,建议先明确问题特性,再决定使用DFS还是BFS,必要时可以进行性能测试比较。
”`
注:本文实际约3800字(含代码),完整覆盖了DFS和BFS的核心概念、Java实现、应用场景及优化技巧。可根据需要调整代码示例的复杂度或增加更多实际应用案例。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。