#Java 数据结构# 图的两种遍历方式

268 阅读4分钟

代码示例

import java.util.*;
public class Main {
	public static void main(String[] args) {
		Graph gra = new Graph(10);
		gra.addEdge(0,2);
		gra.addEdge(2,3);
		gra.addEdge(3,6);
		gra.addEdge(0,3);
		gra.addEdge(0,9);
		gra.addEdge(2,9);
		System.out.println("深度优先遍历");
		gra.DFS(0);
		
		Graph gra2 = new Graph(10);
		gra2.addEdge(0,2);
		gra2.addEdge(2,3);
		gra2.addEdge(3,6);
		gra2.addEdge(0,3);
		gra2.addEdge(0,9);
		gra2.addEdge(2,9);
		System.out.println("广度优先遍历");
		gra2.BFS(0);
	}
}

class Graph{
    
    private int V; 
    private Node adj[];
    
    public Graph(int v){
        V = v;
        adj = new Node[v];
        for(int i=0; i < v; i++){
            Node node = new Node(i);
            adj[i] = node;
        }
        root = adj[0];
    }
    
    public void addEdge(int v, int w){
        if(v<=V && w <= V){
           adj[v].addEdge(w); 
        } else {
           System.out.println("异常数据");
        }
        
    }
    
    // 深度优先遍历
    public void DFS(int v) {
        
        // 将当前顶点设置为已遍历
        adj[v].visited = true;
        Iterator<Integer> i = adj[v].getAdj().listIterator();
        
        while(i.hasNext()){
            int n = i.next();
            if(!adj[n].visited){
                System.out.println(v + "->" + n);
                DFS(n);
            }
        }
    }
    
    
    // 广度优先遍历,在遍历当前节点的下一级节点前,先遍历当前节点的相邻节点
    public void BFS(int v){
        Queue<Integer> queue = new LinkedList<Integer>();
        adj[v].visited = true;
        
        // 添加到队列尾部
        queue.add(v);
        
        while(!queue.isEmpty()){
            // 出列
            int i = queue.poll();
            
            Iterator<Integer> iter = adj[i].getAdj().listIterator();
            while(iter.hasNext()){
                int n = iter.next();
                if(!adj[n].visited){
                    adj[n].visited = true;
                    // 暂时不遍历其下级节点,先遍历相邻节点
                    queue.add(n);
                    System.out.println(i + "->" + n);
                }
                
            }
        }
    }
}

class Node{
    public boolean visited = false;
    private int c;
    private LinkedList<Integer> adj;
    public Node(){
        
    }
    public Node(int v){
        this.c = v;
        this.adj = new LinkedList();
    }
    
    public LinkedList<Integer> getAdj(){
        return adj;
    }
    
    public void addEdge(int n){
        this.adj.add(n);
    }
}

运行结果:

image.png

深度优先遍历(DFS)

优先遍历节点的子节点,再遍历节点的相邻节点。

A -- B -- C
|         |
D -- E -- F

这是一个简单的无向图,其中 ABCDEF 是图中的顶点,-- 表示顶点之间的边。

现在,我们以 A 为起点,来演示如何进行图的深度优先遍历。

深度优先遍历的基本思路是:

从起点开始,访问与之相邻的顶点,并递归地访问与这些相邻顶点相邻的未访问过的顶点,直到所有可达的顶点都被访问过为止。

具体地,以 A 为起点,深度优先遍历的访问顺序如下:

  1. 访问顶点 A,将其标记为已访问。
  2. 访问 A 的相邻顶点 B,将其标记为已访问。
  3. 访问 B 的相邻顶点 C,将其标记为已访问。
  4. C 没有未访问过的相邻顶点,回溯到 B
  5. 访问 B 的相邻顶点 D,将其标记为已访问。
  6. 访问 D 的相邻顶点 E,将其标记为已访问。
  7. 访问 E 的相邻顶点 F,将其标记为已访问。
  8. F 没有未访问过的相邻顶点,回溯到 E
  9. E 没有未访问过的相邻顶点,回溯到 D
  10. D 没有未访问过的相邻顶点,回溯到 B
  11. B 的所有相邻顶点都已被访问,回溯到 A
  12. A 的相邻顶点 D 已经访问过,回溯到结束。

因此,深度优先遍历的访问顺序是 ABCDEF

注意,深度优先遍历使用的是递归方式,因此需要使用栈来记录访问的顶点。

在实际的实现中,也可以使用递归调用来代替显示的栈

广度优先遍历(BFS)

优先遍历节点的相邻节点,再遍历节点的子节点。

A -- B -- C
|         |
D -- E -- F

继续以上图来演示

现在,我们以 A 为起点,来演示如何进行图的广度优先遍历。

广度优先遍历的基本思路是:

从起点开始,访问与之距离为 1 的顶点,并将这些顶点标记为已访问, 然后访问与这些顶点距离为 2 的顶点,将它们标记为已访问,依次类推,直到所有可达的顶点都被访问过止。

具体地,以 A 为起点,广度优先遍历的访问顺序如下:

  1. 访问顶点 A,将其标记为已访问。
  2. 访问 A 的相邻顶点 B,将其标记为已访问,并将其加入队列。
  3. 访问 A 的相邻顶点 D,将其标记为已访问,并将其加入队列。
  4. 从队列中取出下一个顶点 B,访问 B 的相邻顶点 C,将其标记为已访问,并将其加入队列。
  5. 从队列中取出下一个顶点 D,访问 D 的相邻顶点 E,将其标记为已访问,并将其加入队列。
  6. 从队列中取出下一个顶点 CC 没有未访问过的相邻顶点。
  7. 从队列中取出下一个顶点 E,访问 E 的相邻顶点 F,将其标记为已访问,并将其加入队列。
  8. F 没有未访问过的相邻顶点。
  9. 队列为空,结束遍历。

因此,广度优先遍历的访问顺序是 ABDCEF

注意,广度优先遍历使用的是队列,因此需要将已访问过但未访问过相邻顶点的顶点加入队列。

在实际的实现中,也可以使用递归调用来代替队列