【数据结构】图的遍历之DFS和BFS

时间:2021-03-22 12:34:55

1.概述

       图是一种比较复杂的数据结构,一个图G = (V,E)是有顶点集合V和边的集合E组成,在计算机中表示图时一般使用邻接矩阵和邻接表。邻接矩阵一个N*N的矩阵,空间复杂度相对较高,所以一般适用于边的数目比较多的稠密图;邻接表一般使用于边的数目较少稀疏图。

2.算法描述

       图的遍历包括深度优先遍历DFS和广度优先遍历BFS,这两种遍历算法在与图相关的问题中是经常使用的中间算法。

       广度优先遍历与深度优先遍历的区别在于:广度优先遍历是以层为顺序,将某一层上的所有节点都搜索到了之后才向下一层搜索;而深度优先遍历是将某一条枝桠上的所有节点都搜索到了之后,才转向搜索另一条枝桠上的所有节点。        深度优先遍历从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续找它的下一个新的顶点进行访问,重复此步骤,直到所有结点都被访问完为止。        广度优先遍历从某个顶点出发,首先访问这个顶点,然后找出这个结点的所有未被访问的邻接点,访问完后再访问这些结点中第一个邻接点的所有结点,重复此方法,直到所有结点都被访问完为止。        可以看到两种方法最大的区别在于前者从顶点的第一个邻接点一直访问下去再访问顶点的第二个邻接点;后者从顶点开始访问该顶点的所有邻接点再依次向下,一层一层的访问。

3.程序实例

1)一个接口用于回调
/**
*
* 用于访问图中某结点时对此结点进行的操作
*
* @author danDingCongRong
*
* 回调函数的学习和使用
*
*/

public interface Visitor {

public void visit(int k);
}
2)实现图的DFS和BFS
public class Graph {

// 遍历到某结点时对此结点进行的操作(一个接口)
private Visitor visitor;

// 图中结点的个数
private int length;
// 结点集合
private List<Integer> nodeList;
// 邻接矩阵
private List<ArrayList<Integer>> matrix;

/**
* @param nodeList
* 图中各个结点标识
* @param matrix
* 图的邻接矩阵
*/
public Graph(List<Integer> nodeList, List<ArrayList<Integer>> matrix) {
this.nodeList = nodeList;
this.matrix = matrix;
this.length = nodeList.size();
}

/**
* @param visitor
* 遍历到某结点时对其所做的操作
*/
public void setVisitor(Visitor visitor) {
this.visitor = visitor;
}

/**
* <p>深度优先遍历一个图</p>
*
* <li>可以通过DFS判断一个图是否有环</li>
* <li>可以通过DFS判断一个图是否是连通图</li>
* <li>可以通过DFS计算连通子图的个数,即if语句执行的次数</li>
*/
public void DFS() {
// 用于标识结点v[i]是否已遍历,初始值全部为false,即都没有被遍历
boolean[] visited = new boolean[length];
for (int i = 0; i < length; i++) {
if (!visited[i]) {
childDFS(i, visited);
}
}
}

/**
* 遍历一个连通子图
*
* @param k
* 从结点k开始深度优先遍历
* @param visited
* 图中各结点是否已遍历的标志
*/
private void childDFS(int k, boolean[] visited) {
System.out.println(nodeList.get(k));
visited[k] = true;

int weight = 0;
for (int i = 0; i < length; i++) {
weight = matrix.get(k).get(i);
if (!visited[i] && 0 != weight) {
childDFS(i, visited);
}
}
}

public void BFS() {
// 用于标识结点v[i]是否已遍历,初始值全部为false,即都没有被遍历
boolean[] visited = new boolean[length];
// LinkedList实现了Queue接口,所以可以用链表初始化一个队列
Queue<Integer> queue = new LinkedList<Integer>();

for (int i = 0; i < length; i++) {
// 如果结点v[i]没有被遍历,则遍历之并修改对应的访问标识,然后将其进入队列
if (!visited[i]) {
visitor.visit(i);

visited[i] = true;
queue.offer(i);// 入队(插入失败时抛出异常)
}

while (!queue.isEmpty()) {
int k = queue.poll();// 出队并删除队头
for (int j = 0; j < length; j++) {
// 获取结点v[k]与v[j]之间边的权值weight
int weight = matrix.get(k).get(j);

// weight为0表示两顶点间没有边
if (!visited[j] && 0 != weight) {
visitor.visit(j);

visited[j] = true;
queue.offer(j);
}
}
}// while
}// for
}
}

4. 测试代码

/**
* @author danDingCongRong
*
* create on 2014-5-11 17:13:50
*/

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class GraphInstance {

/**
*
* @param vertexCount
* 图中结点的个数
* @return 图中各个结点的标识
*/
public static List<Integer> createVertexList(int vertexCount) {
List<Integer> vertexList = new ArrayList<Integer>();
Scanner scanner = new Scanner(System.in);

for (int i = 0; i < vertexCount; i++) {
vertexList.add(scanner.nextInt());
}

return vertexList;
}

/**
*
* @param n
* 邻接矩阵的大小N*N
* @return 根据用户输入的信息创建的邻接矩阵
*
*/
public static List<ArrayList<Integer>> createGraphMatrix(int vertexNumber,
int edgeNumber) {
List<ArrayList<Integer>> matrix = new ArrayList<ArrayList<Integer>>(
vertexNumber);
for (int i = 0; i < vertexNumber; i++) {
matrix.add(new ArrayList<Integer>(vertexNumber));

for (int j = 0; j < vertexNumber; j++) {
matrix.get(i).add(0);
}
}

int vi = 0, vj = 0, value = 0;
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < edgeNumber; i++) {
vi = scanner.nextInt() - 1;
vj = scanner.nextInt() - 1;
value = scanner.nextInt();

// 无向图的邻接矩阵是对称的
matrix.get(vi).set(vj, value);
matrix.get(vj).set(vi, value);
}

return matrix;
}

public static void main(String[] args) {
System.out.println("请输入图中结点的个数和边的个数:");
Scanner scanner = new Scanner(System.in);
int vertexCount = scanner.nextInt();
int edgeCount = scanner.nextInt();

System.out.println("请输入图中各个结点:");
List<Integer> nodeList = createVertexList(vertexCount);

System.out.println("请输入图的邻接矩阵,格式:vi,vj,weight");
List<ArrayList<Integer>> matrix = createGraphMatrix(vertexCount,
edgeCount);

Graph graph = new Graph(nodeList, matrix);
graph.setVisitor(new Visitor() {

@Override
public void visit(int k) {
System.out.println(k + 1);
}
});

System.out.println("图的DFS:");
graph.DFS();
System.out.println();

System.out.println("图的BFS:");
graph.BFS();
System.out.println();
}
}