圖的深度優先搜尋(DFS)和廣度優先搜尋(BFS)及其Java實現
一、背景知識:
(1)圖的表示方法:鄰接矩陣(二維陣列)、鄰接表(連結串列陣列【連結串列的連結串列】)。
(2)圖的搜尋方法:深度優先搜尋(DFS)和廣度優先搜尋(BFS)。
二、圖的搜尋:
1、深度優先搜尋(DFS):
(1)用棧記錄下一步的走向。訪問一個頂點的過程中要做三件事:
①訪問頂點
②頂點入棧,以便記住它
③標記頂點,以便不會再訪問它
(2)訪問規則:
a.如果可能,訪問一個鄰接的未訪問頂點,標記它,併入棧。
b.當不能執行a時(沒有鄰接的未訪問頂點),如果棧不為空,就從棧中彈出一個頂點。
c.如果不能執行規則a和b,就完成了整個搜尋過程。
(3)實現:基於以上規則,迴圈執行,直到棧為空。每次迴圈各種,它做四件事:
①用peek()方法檢查棧頂的頂點。
②試圖找到這個頂點還未訪問的鄰接點。
③如果沒有找到,出棧。
④如果找到這樣的頂點,訪問併入棧。
比如,採用dfs遍歷上圖,從A點出發,遍歷結果(入棧順序)為:ABCDE。
2、廣度優先搜尋(BFS):
(1)用佇列記錄下一步的走向。深度優先搜素表現的好像是儘快遠離起點似的,相反,廣度優先搜尋中,演算法好像要儘可能靠近起始點。
(2)訪問規則:
a.訪問下一個未訪問的鄰接點(如果存在),這個頂點必須是當前頂點的鄰接點,標記它,併入佇列。
b.如果因為已經沒有未訪問頂點而不能執行規則a,那麼從佇列頭取一個頂點(如果存在),並使其成為當前頂點。
c.如果因為佇列為空而不能執行規則b,則完成了整個搜尋過程。
(3)實現:基於以上規則,對下圖做bfs遍歷,其佇列的變化如下表所示:
從而,遍歷的結果(入隊順序)為ABCDEFGHI。如果採用bfs遍歷3中的圖,結果為:ABDCE。
(4)特性:廣度優先搜尋首先找到與起始點相距一條邊的所有頂點,然後是與起始點相距兩條邊的頂點,以此類推。如果要尋找起始頂點到指定頂點的最短距離,那麼這個屬性非常有用。首先執行BFS,當找到指定頂點時,就可以說這條路徑是到這個頂點的最短路徑。如果有更短的路徑,BFS演算法就應該已經找到它了。
三、Java實現
DFS、BFS的程式碼實現如下:
public class Graph {
private final int MAX_VERTS = 20;
private Vertex vertexList[];// 頂點陣列
private int adjMat[][];// 鄰接矩陣
private int nVerts;// 當前頂點總數
private StackX theStack;
private Queue theQueue;
public static void main(String[] args) {
Graph theGraph = new Graph();
theGraph.addVertex('A');
theGraph.addVertex('B');
theGraph.addVertex('C');
theGraph.addVertex('D');
theGraph.addVertex('E');
theGraph.addEdge(0, 1);
theGraph.addEdge(1, 2);
theGraph.addEdge(0, 3);
theGraph.addEdge(3, 4);
System.out.print("visits:");
// theGraph.dfs();
theGraph.bfs();
System.out.println();
}
public Graph() {// 構造圖
vertexList = new Vertex[MAX_VERTS];
adjMat = new int[MAX_VERTS][MAX_VERTS];
nVerts = 0;
for (int i = 0; i < MAX_VERTS; i++) {
for (int j = 0; j < MAX_VERTS; j++) {
adjMat[i][j] = 0;
}
}
theStack = new StackX();
theQueue = new Queue();
}
public void addVertex(char lab) {// 新增頂點
vertexList[nVerts++] = new Vertex(lab);
}
public void addEdge(int start, int end) {// 新增邊
adjMat[start][end] = 1;
adjMat[end][start] = 1;
}
public void displayVertex(int v) {// 列印陣列中v位置下的頂點名
System.out.print(vertexList[v].lable);
}
public int getAdjUnvisitedVertex(int v) {// 獲取和v鄰接的未訪問的頂點
for (int i = 0; i < nVerts; i++) {
if (adjMat[v][i] == 1 && vertexList[i].wasVisited == false) {
return i;
}
}
return -1;
}
public void dfs() {// 深度優先搜尋
vertexList[0].wasVisited = true;
displayVertex(0);
theStack.push(0);
while (!theStack.isEmpty()) {
int v = getAdjUnvisitedVertex(theStack.peek());
if (v == -1) {
theStack.pop();
} else {
vertexList[v].wasVisited = true;
displayVertex(v);
theStack.push(v);
}
}
for (int i = 0; i < nVerts; i++) {
vertexList[i].wasVisited = false;// 重置,防止後邊再次使用dfs
}
}
public void bfs() {// 廣度優先搜尋
vertexList[0].wasVisited = true;
displayVertex(0);
theQueue.insert(0);
int v2;
while (!theQueue.isEmpty()) {
int v1 = theQueue.remove();
while ((v2 = getAdjUnvisitedVertex(v1)) != -1) {
vertexList[v2].wasVisited = true;
displayVertex(v2);
theQueue.insert(v2);
}
}
for (int j = 0; j < nVerts; j++) {
vertexList[j].wasVisited = false;
}
}
}
class StackX {// 自定義棧
private final int SIZE = 20;
private int[] st;
private int top;
public StackX() {
st = new int[SIZE];
top = -1;
}
public void push(int j) {
st[++top] = j;
}
public int pop() {
if (top == 0) {
return -1;
}
return st[--top];
}
public int peek() {
return st[top];
}
public boolean isEmpty() {
return (top == -1);
}
}
class Queue {
private final int SIZE = 20;
private int[] queArray;
private int front;
private int rear;
public Queue() {
queArray = new int[SIZE];
front = 0;
rear = -1;
}
public void insert(int j) {// 入隊
if (rear == SIZE - 1) {
rear = -1;
}
queArray[++rear] = j;
}
public int remove() {// 出隊
if (!isEmpty()) {
return queArray[front++];
} else {
return -1;
}
}
public boolean isEmpty() {
return (rear + 1 == front);
}
}
class Vertex {
public char lable;// 名字
public boolean wasVisited;
public Vertex(char lab) {
lable = lab;
wasVisited = false;
}
}
輸出:
visits:ABDCE