1. 程式人生 > >圖的深度優先搜尋(DFS)和廣度優先搜尋(BFS)及其Java實現

圖的深度優先搜尋(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