圖的深度遍歷和廣度遍歷
理論部分
圖的深度遍歷和廣度遍歷都不算很難像極了二叉樹的前序遍歷和層序遍歷,如下面的圖,可以用右邊的鄰接矩陣進行表示,假設以頂點0開始對整幅圖進行遍歷的話,兩種遍歷方式的思想如下:
1. 深度優先遍歷(depthFirstSearch—DFS)
由初始頂點開始,沿著一條道一直走,當走到走不動的時候,再回來走一條可以走的通的道,然後再繼續往下走,直到走不動,再回來…對應於本圖來說就是從0開始往前走,到1----->然後從1再往前走,到5----->從5再往前走,到4------->到了這裡發現沒路可走了------>就往回走,回到5,看5還有沒有路,發現沒路----->則回到1,看1有沒有路,也沒有----->再回到0,看0有沒有路,發現有------>則由0走到3----->走到這裡發現又沒有路了----->再往回走,走到0,看0還有沒有路,發現有----->則由0走到4,但是4已經被遍歷過了,所以再回到0,結束這次遍歷過程
但是這時候還有一個2沒有遍歷啊,該怎麼辦呢?之前我們是直接就預設從0開始進行往下遍歷了,但是從0開始遍歷沒有一條路可以走到2,為了避免這種情況,我們必須得從每一個頂點開始遍歷,這樣才能避免漏掉這種只出不進的頂點
於是深度優先遍歷得到的遍歷結果應為:0 1 5 4 3 2
2.廣度優先遍歷(broadFirstSearch—BFS)
廣度遍歷我覺得理解起來更簡單,就是一層一層的進行遍歷,比如說以0頂點開始,0往下指向1,3,4,遍歷的時候就先遍歷0,然後再遍歷它下一層的1,3,4------>然後分別遍歷1,3,4的下一層---->而1,3,4只有1有下一層,則遍歷1的下一層5,同理最後遍歷2
即廣度優先遍歷得到的遍歷結果應為:0 1 3 4 5 2
和二叉樹的層序遍歷一樣,圖的廣度遍歷也用到了佇列,對於下圖而言,先將0放入隊首----->然後遍歷0並將0從佇列中取出,同時將0的鄰接點1,3,4入隊,這樣隊首就是1----->然後將1出隊,並將1的鄰接點入隊(這裡只有5), 這樣隊首就是3----->然後將3彈出並將3的鄰接點入隊(這裡沒有),這樣隊首就是4----->然後將4彈出並將4的鄰接點入隊(這裡沒有),隊首就是從1入隊的1的第一個鄰接點(這裡是5)---->然後將5彈出----->直到佇列為空這樣就完成了由定點0開始的廣度優先遍歷
我寫的程式碼如下:
package cn.nrsc.graph;
import java.util.LinkedList;
import java.util.Queue;
//圖的遍歷
public class Graph {
// ----------------------------圖的表示方式------------------------
private int vertexSize;// 頂點的數量
private int[] vertexs;// 頂點對應的陣列
private int[][] matrix;// 鄰接矩陣
private static final int MAX_WEIGHT = 1000;// 代表頂點之間不連通
private boolean[] isVisited; // 頂點是否已經被訪問
public Graph(int vertexSize) {
this.vertexSize = vertexSize;
this.matrix = new int[vertexSize][vertexSize];
this.vertexs = new int[vertexSize];
for (int i = 0; i < vertexSize; i++) {
vertexs[i] = i;
}
isVisited = new boolean[vertexSize];
}
// ----------------------------圖的表示方式------------------------
// ----------------------------兩個重要方法------------------------
// 獲取某個頂點的第一個鄰接點
public int getFirstNeighbor(int index) {
for (int i = 0; i < vertexSize; i++) {
if (matrix[index][i] > 0 && matrix[index][i] != 1000) {
return i;
}
}
return -1; // 如果沒有第一個鄰接點,則返回-1
}
// 獲取某個節點V當前鄰接點index的下一個鄰接點
public int getNextNeighbor(int v, int index) {
for (int i = index + 1; i < vertexSize; i++) {
if (matrix[v][i] > 0 && matrix[v][i] != 1000) {
return i;
}
}
return -1; // 如果沒有返回-1
}
// ----------------------------兩個重要方法------------------------
// 圖的深度優先遍歷演算法 ---- 從頂點i進行遍歷
private void depthFirstSearch(int i) {
// 開始遍歷頂點i---所以將其標記為已經遍歷了
isVisited[i] = true;
// 遍歷頂點i的第一個鄰接點
int FN = getFirstNeighbor(i);
while (FN != -1) {// 如果第一個鄰接點存在
if (!isVisited[FN]) { // 且第一個鄰接點沒被遍歷遍歷過
System.out.println("遍歷到了" + FN + "頂點"); // 遍歷該頂點
// 同時遞迴遍歷該頂點的鄰接點
depthFirstSearch(FN);
}
// 如果第一個鄰接點已經遍歷過了---則遍歷其第一個鄰接點後面的鄰接點
FN = getNextNeighbor(i, FN);
}
}
// 對外提供的深度優先遍歷
public void depthFirstSearch() {
// 假如圖為有向圖-----可能遍歷到一定程度就再也走不通了
// 如我例子中的節點2---所以需要對每一個節點進行一下迴圈
for (int i = 0; i < vertexSize; i++)
if (!isVisited[i]) {
System.out.println("遍歷到了" + i + "頂點");
depthFirstSearch(i);
}
}
// 對外提供的廣度優先遍歷
public void broadFirstSearch() {
// 假如圖為有向圖-----可能遍歷到一定程度就再也走不通了
// 如我例子中的節點2---所以需要對每一個節點進行一下迴圈
for (int i = 0; i < vertexSize; i++)
if (!isVisited[i]) {
broadFirstSearch(i);
}
}
private void broadFirstSearch(int i) {
Queue<Integer> queue = new LinkedList<>();
// 將遍歷到的i壓人佇列中
queue.add(i);
isVisited[i] = true; // 標記該頂點已經被遍歷過
System.out.println("遍歷到了" + i + "頂點");
while (!queue.isEmpty()) {
// 彈出隊首的元素
Integer head = queue.poll();
// 獲取隊首元素第一個鄰接點的元素
int FN = getFirstNeighbor(head);
while (FN != -1) { // 如果有第一個鄰接點
if (!isVisited[FN]) {// 且該鄰接點沒有被訪問過
isVisited[FN] = true;// 標記該頂點已經被遍歷過
System.out.println("遍歷到了" + FN + "頂點");
queue.add(FN); // 讓FN進人佇列
}
// 遍歷佇列首元素head基於FN的下一個元素
FN = getNextNeighbor(head, FN);
}
}
}
public static void main(String[] args) {
Graph graph = new Graph(6);
int[] v0 = { 0, 4, MAX_WEIGHT, 7, 2, MAX_WEIGHT };
int[] v1 = { MAX_WEIGHT, 0, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 3 };
int[] v2 = { MAX_WEIGHT, 9, 0, 5, 6, MAX_WEIGHT };
int[] v3 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0, MAX_WEIGHT, MAX_WEIGHT };
int[] v4 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0, MAX_WEIGHT };
int[] v5 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 1, 0 };
graph.matrix[0] = v0;
graph.matrix[1] = v1;
graph.matrix[2] = v2;
graph.matrix[3] = v3;
graph.matrix[4] = v4;
graph.matrix[5] = v5;
// 深度優先遍歷測試
// graph.depthFirstSearch(); // 0 1 5 4 3 2
// 廣度優先遍歷測試
graph.broadFirstSearch();// 0 1 3 4 5 2
}
}