深度优先搜索DFS即Depth First Search。其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。广度优先搜索BFS是Breadth First Search。所有因为展开节点而得到的子节点都会被加进一个先进先出的队列中。
DFS/BFS搜索算法分析
定理一:深度优先搜索标记与起点连通的所有顶点所需的时间和顶点的度数之和成正比。
假设我们有两个点v和w,在一张图中,当v先访问到w时,v-w这条边从未检查的状态变为已检查,此时这条边访问了一次。当w访问v时,w-v这条边又被检查一次,但是发现已经被检查,此时这条边被访问了两次。除此之外,不可能再有其他情况导致v-w(w-v)这条边被访问。所以可得,图中每条边会被访问两次,边×2 = 顶点 Σ 度数 (各顶点度数之和)。所以成正比。
定理二:一般的,使用深度优先搜索能解决的问题都可转化为广度优先搜索解决。
深度优先搜索的优点在于:递归易于理解、简单。但是深度优先搜索并没有明确的目的性,而广度优先搜索按照由近及远的顺序搜索,在很多情况下能找出最优解,而且循环的效率高于递归,并没有栈溢出的风险。在稀疏图中广度优先搜索的效率更是快过深度优先搜索很多,稠密图相差无几。在不一定需要广度优先搜索的情况下,我们可以尽量使用深度优先搜索。
定理三:在使用邻接表作为图记录方法时,深度优先搜索与广度优先搜索时间复杂度均为O(V+E)。
访问元素所需的时间主要取决于图数据的记录方法,无论是深度优先搜索或是广度优先搜索,都需要检查整张图后才能计算完毕,耗时的主要部分取决于记录方式,在使用邻接矩阵作为记录数据的方法时,复杂度为O(n2),而邻接表中只有(顶点+边数×2)的数据,我们只需要执行其中一半的边数,另一半可由检查免除运算。所以,在使用邻接表作为图记录方法时,DFS与BFS时间复杂度均为O(V+E)。
基本数据结构——图类
深度优先算法和广度优先算法是基于图论的算法。在实现应用之前,先实现基本的无向图类数据结构。
Graph类用V定义定点,E定义边,LinkedList<Integer>[ ]定义邻接表。
package Graph;
import java.util.LinkedList;
public class Graph {
private final int V;
private int E;
private LinkedList<Integer>[] adj;
public Graph(int V) {
this.V = V;
this.E = 0;
adj = (LinkedList<Integer>[]) new LinkedList[V];
for (int v = 0; v < V; v++)
adj[v] = new LinkedList<>();
}
public int V() {
return V;
}
public int E() {
return E;
}
public void addEdge(int v, int w) {
adj[v].add(w);
adj[w].add(v);
E++;
}
public LinkedList<Integer> adj(int v) {
return adj[v];
}
public int degree(int v,Graph g){
int count = 0;
for(int s : adj(v))
count++;
return count;
}
}