You can't have a better tomorrow if you are thinking about yesterday.
若是你還在思考昨天,那麼你就沒法擁有一個更好的明天。
圖是一種比較複雜的非線性數據結構。圖分不少種,無向圖,有向圖,帶權圖,稀疏圖等等。本文主要分享了無向圖的兩種暴力搜索算法BFS(廣度優先搜索)和DFS(深度優先搜索)。全部源碼均已上傳至github: 連接git
private Graph(int count) {
this.count = count;
adj = new LinkedList[count];
for (int i = 0; i < adj.length; i++) {
adj[i] = new LinkedList<>();
}
}
複製代碼
private void add(int oSide, int rSide) {
adj[oSide].add(rSide);
adj[rSide].add(oSide);
}
複製代碼
private void createGraph() {
// 0 -- 1 -- 2
// | | |
// 3 -- 4 -- 5
// | | |
// 6 -- 7 -- 8
add(0,1);//add(1,0);
add(0,3);//add(3,0);
add(1,2);//add(2,1);
add(1,4);// add(4,1);
add(2,5);//add(5,2);
add(3,4);//add(4,3);
add(3,6);//add(6,3);
add(4,5);//add(5,4);
add(4,7);// add(7,4);
add(5,8);//add(8,5);
add(6,7);//add(7,6);
add(7,8);//add(8,7);
}
複製代碼
準備完畢github
廣度優先搜索,從字面意思理解,它就是一種「地毯式」的搜索策略,先查找離起始頂點最近的,而後是次近的,依次往外搜索,層層遞進。算法
在這裏三個重要的核心輔助變量 visited、queue、prev。bash
這裏看一下它的打印方式就理解prev的存儲機制了。數據結構
private void print(int[] prev, int oSide, int rSide) {
if (prev[rSide] != -1 && oSide != rSide) {
print(prev, oSide, prev[rSide]);
}
System.out.print(rSide + " ");
}
複製代碼
首先先將起點oSide加入隊列queue,而後在該while循環裏,遍歷該節點的鄰接表,再經過visited進行判斷,看是否訪問,一直到遍歷出的value == rSide 說明已找到,不然讓visited 記錄該頂點已訪問,同時入隊,就這樣經過不停地入隊出隊。ide
ps:遍歷出的圖的路徑也是它的最短路徑。測試
private void bfs(int oSide, int rSide) {
if (oSide == rSide) return;
boolean[] visited = new boolean[count];
visited[oSide] = true;
Queue<Integer> queue = new LinkedList<>();
queue.offer(oSide);
int[] prev = new int[count];
for (int i = 0; i < count; i++) {
prev[i] = -1;
}
while (!queue.isEmpty()) {
int index = queue.poll();
for (int j = 0; j < adj[index].size(); j++) {
int value = adj[index].get(j);
if (!visited[value]) {
prev[value] = index;
if (value == rSide) {
print(prev, oSide, rSide);
}
visited[value] = true;
queue.offer(value);
}
}
}
}複製代碼
深刻優先搜索,有點走迷宮的意思,隨機選擇路徑,當發現走不通的時候,退回來從新來過,直到找到終點。經過測試結果也能夠看出它把全部的路徑都訪問了一遍,蛇皮走位。它用到了一種十分著名的思想--回溯思想。就像是'後悔藥'。ui
它和bfs相同的是均用到了prev、visited。不一樣的是,它聲明瞭一個全局的變量found,起到一個跳出遞歸的做用。this
它的關鍵在於recursiveDfs這個遞歸方法。默認起點被訪問,先聲明一個出口,當知足查找的頂點等於終點的時候跳出。遍歷當前頂點的鄰接表,逐個判斷頂點是否已訪問,記錄下來,而後遞歸。spa
ps:遍歷出的圖的路徑也是它的最長路徑。
private boolean found = false;
private void dfs(int oSide, int rSide) {
boolean[] visited = new boolean[count];
int[] prev = new int[count];
for (int i = 0; i < count; i++) {
prev[i] = -1;
}
recursiveDfs(visited, prev, oSide, rSide);
print(prev, oSide, rSide);
}
private void recursiveDfs(boolean[] visited, int[] prev, int oSide, int rSide) {
if (found) return;
visited[oSide] = true;
if (oSide == rSide) {
found = true;
return;
}
for (int i = 0; i < adj[oSide].size(); i++) {
int value = adj[oSide].get(i);
if (!visited[value]) {
prev[value] = oSide;
recursiveDfs(visited, prev, value, rSide);
}
}
}
複製代碼
public static void main(String[] args) {
int count = 9;
Graph graph = new Graph(count);
// 0 -- 1 -- 2
// | | |
// 3 -- 4 -- 5
// | | |
// 6 -- 7 -- 8
graph.createGraph();
System.out.println("BFS(廣度優先搜索)");
graph.bfs(0,8);
System.out.println();
System.out.println("DFS(深度優先搜索)");
graph.dfs(0,8);
}複製代碼
您的點贊和關注是對我最大的支持,謝謝!