1. 程式人生 > >資料結構之深度優先遍歷

資料結構之深度優先遍歷

深度優先遍歷(DSF):

思想:可以使用遞迴和非遞迴來完成dsf

簡述一下非遞迴方法的思想:是使用佇列還是棧呢?首先要明確佇列和棧各自的特點,佇列是先進先出,
棧是後進先出出,想一想深度優先檢索,重點是在深度,要想深度向下,就需要沿著一條線從一個節點一直向下
遍歷直到沒有節點可遍歷位置。那麼問題來了,要想沿著一條線不斷向下,就需要不斷的找起始節點的下一個節點
再下一個節點,再再下一個節點......直到最後,可以通過一個節點的鄰接節點來找到其“下一個節點”,
所以使用棧,從第一個節點開始,不斷的入棧彈棧,利用棧的後進先出的特點來找出節點的某一鄰接節點作為
遍歷路徑上的下一個節點

接下來上張圖來說明深度遍歷


比如從A開始,找A的鄰接節點有:B,F比如選中B作為A的下一個節點,此時A已經遍歷過,遍歷過的節點以後便不用再遍歷
再看B的鄰接節點有C,I,G選擇C作為B的下一個節點,此時B也已經遍歷過了,一次類推比如遍歷的順序為A,B,C,D,E,F,G,H,
沒有能往下繼續遍歷的節點,所以開始回溯,看是否還有節點沒有遍歷完,首先回溯到節點G,發現G的鄰接節點已經
完全遍歷完了,在回溯到F,同樣如此,然後F,E,D,C依次回溯,到C發現C的鄰接節點I沒有遍歷,所以把I作為下一個節點開始遍歷
,遍歷完再回溯,直達回溯到A節點為止。所以應為再回溯的過程中要找上一個節點,所以用棧非常適合深度優先遍歷

深度優先遍歷既可以使用遞迴方法又可以使用非遞迴方法

下面呈上一段詳細程式碼

package SevenProvience.tital2;

import java.util.Stack;

public class DFS {
	//儲存節點資訊
	private char[] vertices;
	//儲存邊資訊(鄰接矩陣)
	private int[][] arcs;
	//圖的節點數
	private int vexnum;
	//記錄節點是否已被遍歷
	private boolean[] visited;
	//初始化
	public DFS(int n){
		vexnum=n;
		vertices=new char[n];
		arcs=new int[n][n];
		visited=new boolean[n];
		//將矩陣所有的邊都初始化為0
		for(int i=0;i<vexnum;i++){
			for(int j=0;j<vexnum;j++){
				arcs[i][j]=0;
			}
		}
	}
	//新增邊(無向圖)
	public void addEdge(int i,int j){
		//邊的頭尾不能為同一個節點 
		if(i==j)return;
		arcs[i][j]=1;
		arcs[j][i]=1;
	}
	//設定節點集
	public void setVertices(char[] vertices){
		this.vertices=vertices;
	}
	//設定節點訪問標記
	public void setVisited(boolean[] visited){
		this.visited=visited;
	}
	//列印遍歷節點
	public void visit(int i){
		System.out.print(vertices[i]+" ");
	}
	//非遞迴方法是實現深度優先遍歷
	public void DFSTraverse(){
		//初始化節點遍歷標記
		for(int i=0;i<vexnum;i++){
			visited[i]=false;
		}
		//建立一個棧
		Stack<Integer> s=new Stack<Integer>();
		//對非聯通圖執行迴圈進行遍歷以至於所有節點都遍歷到
		//若是連通圖該for迴圈只執行一次
		for(int i=0;i<vexnum;i++){
			if(!visited[i]){
				//連通子圖起始節點
				s.add(i);
				do{
					//出棧
					int curr=s.pop();
					//如果節點還沒有被遍歷到,則遍歷該節點並將該節點入棧用來遍歷其子節點
					if(visited[curr]==false){
						//遍歷並列印
						visit(curr);
						visited[curr]=true;
						//沒遍歷的子節點入棧
						for(int j=vexnum-1;j>=0;j--){
							if(arcs[curr][j]==1&&visited[j]==false){
								s.add(j);
							}
						}
					}
				}while(!s.isEmpty());
			}	
		}
	}
	//從第i個節點開始深度優先遍歷
	private void traverse(int i){
		//標記第i個節點已被遍歷
		visited[i]=true;
		//列印當前遍歷的節點
		visit(i);
		//遍歷鄰接矩陣中第i個節點的直接連通關係
		for(int j=0;j<vexnum;j++){
			//目標節點與當前節點直接連通並沒有被訪問,遞迴遍歷
			if(arcs[i][j]==1&&visited[j]==false){
				traverse(j);
			}
		}
	}
	//圖的深度優先遍歷(遞迴法)
	public void DFSTraverse1(){
		//初始化節點遍歷標記
		for(int i=0;i<vexnum;i++){
			visited[i]=false;
		}
		//從沒有被遍歷的節點開始深度遍歷
		//連通圖的話這個迴圈只會執行一次
		for(int i=0;i<vexnum;i++){
			if(visited[i]==false)
			traverse(i);
		}
	}
	
}
package SevenProvience.tital2;

public class test {

	public static void main(String[] args) {
		DFS g = new DFS(9);
        char[] vertices = {'A','B','C','D','E','F','G','H','I'};
        g.setVertices(vertices);

        g.addEdge(0, 1);
        g.addEdge(0, 5);
        g.addEdge(1, 0);
        g.addEdge(1, 2);
        g.addEdge(1, 6);
        g.addEdge(1, 8);
        g.addEdge(2, 1);
        g.addEdge(2, 3);
        g.addEdge(2, 8);
        g.addEdge(3, 2);
        g.addEdge(3, 4);
        g.addEdge(3, 6);
        g.addEdge(3, 7);
        g.addEdge(3, 8);
        g.addEdge(4, 3);
        g.addEdge(4, 5);
        g.addEdge(4, 7);
        g.addEdge(5, 0);
        g.addEdge(5, 4);
        g.addEdge(5, 6);
        g.addEdge(6, 1);
        g.addEdge(6, 3);
        g.addEdge(6, 5);
        g.addEdge(6, 7);
        g.addEdge(7, 3);
        g.addEdge(7, 4);
        g.addEdge(7, 6);
        g.addEdge(8, 1);
        g.addEdge(8, 2);
        g.addEdge(8, 3);
        
        System.out.println("深度優先遍歷(非遞迴):");
        g.DFSTraverse();
        System.out.println();
        System.out.println("深度優先遍歷(遞迴):");
        g.DFSTraverse1();
	}

}