1. 程式人生 > >圖的遍歷演算法(DFS和BFS)

圖的遍歷演算法(DFS和BFS)

1.    圖的深度遍歷DFS

可採用遞迴和迴圈兩種方式實現。

方法一:採用遞迴的方式。

定義一個標誌陣列表示某個結點是否已經被訪問過。以鄰接矩陣的形式表示圖。依次對深度遍歷圖中的每個未被遍歷過的結點,然後針對該結點未被遍歷過的鄰接點再進行深度遍歷,每個結點被遍歷後加入到結果中,並且設定該結點已經被訪問過。

方法二:不採用遞迴的方式而是採用棧儲存已經遍歷過並且還有其孩子結點可能還沒有遍歷過的結點。與遞迴方式中一樣,也是定義一個標誌陣列表示某個結點是否已經被訪問過。始化棧記憶體放第一個結點。判斷棧頂元素的第一個還未被訪問鄰接點,把它壓入到棧中,每個結點入棧的時候,把其對應的標誌陣列元素設為true同時把該結點放到結果中。如果棧頂元素沒有了未被訪問的鄰接點,則將該元素彈棧。迴圈直到棧空。

/**
	 * 方法一:採用遞迴的方式。
	 * 定義一個標誌陣列表示某個結點是否已經被訪問過。
	 * 以鄰接矩陣的形式表示圖。
	 * 依次對深度遍歷圖中的每個未被遍歷過的結點,針對該結點未被遍歷過的鄰接點再進行深度遍歷,每個結點被遍歷後加入到結果中,並且設定該結點已經被訪問過。
	 */
	public static boolean visited[];
	public static ArrayList<Integer> list = new ArrayList<Integer>();
	public static ArrayList<Integer> DFS_Graph1(int graph[][]){
		int size = graph.length;//求得結點的個數
		visited = new boolean[size];
		/*Step1:依次遍歷樹中還未被遍歷的結點*/
		for(int i=0;i<size;i++){
			if(visited[i] == false){
				visited[i] = true;
				list.add(i);
				/*Step2:深度遍歷以結點i為開始的路徑上的結點*/				          DFS(graph,i);
			}
		}
		return list;		
	}
	/**
	 * 深度遞迴遍歷以圖中node結點為起點的結點。
	 * @param graph
	 * @param node
	 */
	public static void DFS(int graph[][],int i){
		int size = graph.length;
		for(int j=0;j<size;j++){
			/*這個當前結點可達且未被訪問的結點j才可以*/
			if(graph[i][j] == 1 && visited[j] == false){
				visited[j] = true;
				list.add(j);
				DFS(graph,j);
			}
		}
	}
	
	/**
	 * 方法二:不採用遞迴的方式而是採用棧儲存已經遍歷過並且還有其孩子結點可能還沒有遍歷過的結點。
	 * 與遞迴方式中一樣,也是定義一個標誌陣列表示某個結點是否已經被訪問過。
	 * 初始化棧記憶體放第一個結點。
	 * 判斷棧頂元素的第一個還未被訪問鄰接點,把它壓入到棧中,每個結點入棧的時候,把其對應的標誌陣列元素設為true同時把該結點放到結果中。
	 * 如果棧頂元素沒有了未被訪問的鄰接點,則將該元素彈棧。
	 * 一直迴圈直到棧空。
	 * 
	 * @param graph
	 * @return
	 */
	public static ArrayList<Integer> DFS_Graph2(int graph[][]){
		Stack<Integer> stack = new Stack<Integer>();
		int size = graph.length;
		visited = new boolean[size];
		/*Step1:把初始第一個結點放到棧內*/
		stack.push(0);
		visited[0] = true;
		list.add(0);
		/*	
		 * Step2:找棧頂元素的第一個還未被訪問鄰接點,把它壓入到棧中,
		 * 每個結點入棧的時候,把其對應的標誌陣列元素設為true同時把該結點放到結果中。 
		 * 如果棧頂元素沒有了未被訪問的鄰接點,則將該元素彈棧。
		 * 迴圈結束的條件是棧為空。
		 */
		while(!stack.isEmpty()){
			int i = stack.peek();//讀棧頂元素
			int j = 0;
			/*找棧頂元素的第一個未被訪問的鄰接點*/
			for(;j<size;j++){
				if(graph[i][j] == 1 && visited[j] == false){
					break;
				}
			}
			if(j<size){//說明找到了
				visited[j] = true;
				list.add(j);
				stack.push(j);
			}else{//只有當前棧頂元素不存在未被訪問的鄰接點時才彈出棧頂元素
				stack.pop();
			}
		}
		return list;
	}

2. 圖的廣度優先遍歷

       藉助佇列實現,同時設定一個包含結點個數的visited陣列表示某個結點是否已經被遍歷過。

       初始化時把第一個結點加入到結果list中,並且把visited[0]設定為true。

       迴圈開始時佇列中儲存的是圖中的第一個結點,迴圈結束的條件是佇列變成空。

       迴圈體內取出佇列的第一個元素,並且把這個元素的所有未被訪問過的鄰接點都加入到佇列中、加入到結果list中、設定其對應的狀態為已訪問。

      類似於樹的層次遍歷.

/**
	 * 圖的廣度優先遍歷。
	 * 藉助佇列實現,同時設定一個包含結點個數的visited陣列表示某個結點是否已經被遍歷過。
	 * 初始化時把第一個結點加入到結果list中,並且把visited[0]設定為true。
	 * 迴圈開始時佇列中儲存的是圖中的第一個結點,迴圈結束的條件是佇列變成空。
	 * 迴圈體內取出佇列的第一個元素,並且把這個元素的所有未被訪問過的鄰接點都加入到佇列中、加入到結果list中、設定其對應的狀態為已訪問。
	 */
	public static boolean visited[];
	public static ArrayList<Integer> list = new ArrayList<Integer>();
	public static ArrayList<Integer> BFS_Graph(int graph[][]){
		Queue<Integer> queue = new ArrayBlockingQueue<Integer>(10);
		int size = graph.length;
		visited = new boolean[size];
		/*Step1:把初始第一個結點放到棧內。*/
		queue.add(0);
		visited[0] = true;
		list.add(0);
		int i ;
		/*Step2:迴圈佇列中的元素直到佇列為空。*/
		while(!queue.isEmpty()){
			i = queue.poll();
			for(int j =0;j<size;j++){//找剛剛被彈出佇列的隊頭元素可達的點
				if(graph[i][j] ==1 && visited[j] == false){
					visited[j] = true;
					list.add(j);
					queue.add(j);
				}
			}
		}
		return list;
	}