圖的遍歷之DFS與BFS
阿新 • • 發佈:2018-12-18
圖的遍歷
圖的遍歷指的是從圖中的任一頂點出發,對圖中的所有頂點訪問一次且只訪問一次。圖的遍歷操作和樹的遍歷操作功能相似。圖的遍歷是圖的一種基本操作,圖的許多其它操作都是建立在遍歷操作的基礎之上。 根據訪問節點的順序,我們可以分成兩種方法來對圖進行遍歷。分別是深度優先遍歷(DFS)和廣度優先遍歷(BFS)。
DFS
演算法思想:
從某個點一直往深處走,走到不能往下走之後,就回退到上一步,直到找到解或把所有點走完。
演算法步驟(遞迴或棧實現)
- 訪問指定起始地點。
- 若當前訪問頂點的鄰接頂點有未被訪問的頂點,就任選一個訪問。如果沒有就回退到最近訪問的頂點,直到與起始頂點相通的所有點被遍歷完。
- 若途中還有頂點未被訪問,則再選一個點作為起始頂點。重複步驟2(針對非連通圖)。
演算法實現
import java.util.Stack; public class Demo_01_DFS { //遞迴版DFS public static void DFSbyRecursion(int[][] graph){ int length = graph.length; boolean[] visited = new boolean[length]; //為了預防圖不是連通圖的情況,若圖為連通圖則直接呼叫DFS,不需要for迴圈 for (int i = 0; i < length; i++) { if(!visited[i]) DFS(graph,i,visited); } } public static void DFS(int[][] graph,int vertex,boolean[] visited){ visited[vertex] = true; //遍歷該點 System.out.print(vertex + " "); int length = graph.length; for (int i = 0; i < length; i++) { //找出與vertex相鄰的點,進行DFS。找到一個點就DFS,遍歷到底了就進行回退(這裡注意遞迴的過程,) if(!visited[i] && graph[vertex][i] == 1){ DFS(graph,i,visited); } } } //用棧實現DFS public static void DFSbyStack(int[][] graph){ Stack<Integer> stack = new Stack<>(); int length = graph.length; //判斷元素是否被遍歷過 boolean[] visited = new boolean[length]; for (int i = 0; i < length; i++) { if(!visited[i]){ stack.push(i); visited[i] = true; boolean hasNext; //遍歷第一個點 System.out.print( i+ " "); while(!stack.empty()){ //取出棧頂元素 int temp = stack.peek(); //設定變數來判斷是否有新點入棧,沒有就彈出棧頂元素,有的話進行下一次迴圈。 hasNext = false; for (int j = 0; j < length; j++) { //找出一個與棧頂元素有連線且沒有被遍歷的點放入stack中,並遍歷該點 if(!visited[j] && graph[temp][j] == 1){ stack.push(j); visited[j] = true; hasNext = true; //遍歷該點 System.out.print(j + " "); break; } } //如果沒有下一個元素則回溯,刪除棧頂元素 if (!hasNext){ stack.pop(); } } } } } public static void main(String[] args) { int[][] graph = { { 0, 1, 1, 0, 0 }, { 0, 0, 1, 0, 1 }, { 0, 0, 0, 0, 0 }, { 1, 1, 0, 0, 1 }, { 0, 0, 1, 0, 0 } }; DFSbyRecursion(graph); System.out.println(); DFSbyStack(graph); } }
BFS
演算法思想
從某個點一直把其鄰接點走完,然後任選一個鄰接點把與之鄰接的未被遍歷的點走完,如此反覆走完所有結點。類似於樹的層序遍歷。
演算法步驟(用佇列實現)
- 訪問指定起始點。
- 訪問當前頂點的鄰接頂點有未被訪問的頂點,並將之放入佇列中。
- 刪除佇列的隊首節點。訪問當前佇列的隊首,重複步驟2。直到佇列為空。
- 若若途中還有頂點未被訪問,則再選一個點作為起始頂點。重複步驟2。(針對非連通圖)。
演算法實現
import java.util.LinkedList; import java.util.Queue; public class Demo_02_BFS { //用佇列來實現BFS public static void BFSbyQueue(int[][] graph){ Queue<Integer> queue = new LinkedList<>(); int length = graph.length; boolean[] visited = new boolean[length]; //為了預防圖不是連通圖的情況,若圖為連通圖則不需要for迴圈 for (int i = 0; i < length; i++) { if(!visited[i]){ queue.add(i); visited[i] = true; System.out.print(i + " "); while(queue.size() != 0){ int temp = queue.poll(); //遍歷所有與temp相鄰的點,依次加入佇列中,並遍歷他們 for (int j = 0; j < length; j++) { if(!visited[j] && graph[temp][j] == 1){ queue.add(j); visited[j] = true; System.out.print(j + " "); } } }//while } } } public static void main(String[] args) { int[][] graph = { { 0, 1, 0, 1, 0 }, { 0, 0, 1, 0, 1 }, { 0, 0, 0, 0, 0 }, { 1, 1, 0, 0, 1 }, { 0, 0, 1, 0, 0 } }; BFSbyQueue(graph); } }