1. 程式人生 > >圖的基本概念表示方法以及兩種搜尋方式——深度優先遍歷和廣度優先遍歷

圖的基本概念表示方法以及兩種搜尋方式——深度優先遍歷和廣度優先遍歷

原先的知識沒好好學,導致現在很多都忘了,或者說以前就沒弄懂過。現在重新看一遍,收穫良多,不管怎樣先寫這篇基礎的,當做筆記。

圖的定義:是由頂點的有窮非空集合和頂點之間的邊的集合組成的,通常表示為 G(V,E)。其中G表示一個圖,V是圖的頂點的集合,E是圖的邊的集合。

有跟多條邊的圖我們稱為稠密圖,很少條邊的我們稱為稀疏圖。

有向圖和無向圖:

無向圖:頂點之間的邊是沒有方向的,也就是兩個方向互通的。比如頂點 Vi 和頂點 Vj 之間的邊,用(Vi,Vj)表示。

有向圖:頂點間的邊是有方向的,稱為有向邊,也成為 弧(Arc)。比如頂點Vi指向頂點Vj 的弧,用有序的偶:

<Vi,Vj> 表示,Vi稱為弧尾,Vj 稱為弧頭。

如果無向圖中任意兩個頂點之間都存在邊,則稱該圖是無向完全圖。n 個頂點的無向圖有 (n - 1)*n / 2條邊。

與圖的邊或者弧有關的數叫做權(權值,Weight),帶權的圖通常稱為網。

連通圖:如果頂點Vi 到Vj 有路徑,則稱Vi 和Vj 是連通的。如果對於圖中的任意兩個頂點 Vi 和 Vj 是連通的則稱該圖為連通圖。

無向圖中的極大連通子圖稱為連通分量。

度:一個頂點所連線的邊的數目,是對無向圖而言的

入度和出度:一個頂點指向其他頂點的弧的數目稱為出度,其他頂點指向該頂點的弧的數目稱為入度。

圖的儲存結構:鄰接矩陣、鄰接表、十字連結串列臨街多重表等。

鄰接矩陣的儲存方式:

用一個一維的陣列來儲存儲存圖中的頂點資訊,用一個二維的陣列來儲存圖中邊的或者弧的資訊。

鄰接矩陣簡單的定義圖的結構:

public class graph {
	
	public char[] vexs;		//頂點
	public int[][] arc;		//鄰接矩陣
	int numVexs;			//頂點數
	int numEdges;			//邊數

	public graph(int nVexs,int nEdg){
		this.numVexs = nVexs;
		this.numEdges = nEdg;
	}
}
我們先用一位陣列 vexs 儲存 n 個頂點的資訊,然後一個 n*n 的二維陣列 arc 儲存邊或者弧。arc[ i ][ j ] 等於 1 表示頂點 Vi 連通 Vj (如果是無向圖那麼 arc[ j ][ i ] 也為 1),等於 0 則說明這兩個頂點之間沒有邊或者弧 。
無向圖的鄰接矩陣是對稱的。 鄰接矩陣是一種不錯的儲存圖的資料結構,但是當頂點相對較多而邊相對較少的時候,二維的鄰接矩陣顯得有點浪費。 所以這裡引入鄰接表:用一個一維陣列儲存頂點,頂點的鄰接點構成一個線性表。我們把這種陣列和線性表相組合的儲存方法稱為鄰接表 十字連結串列、多重連結串列就不說了。 接下來重點說圖的遍歷:從圖中的某一頂點出發訪問圖中的其餘頂點,且每個頂點僅訪問一次,這 一過程稱為圖的遍歷。 深度優先遍歷(Depth First Search):也稱為深度優先搜尋,DFS,深度優先搜尋其實是一個遞迴過程,一條路先走到底有,點像二叉樹的先序遍歷。我們需要一個數組 visited 來標記已經訪問過的頂點 ,visited[ i ] = false 表示未訪問,true 表示已經訪問過。 具體實現:
package com.hunter.Graph;
/**
 * 深度優先搜尋
 * @author luzi
 *
 */
public class graphDFS {
	
	public void DFSraverse(graph gh){
		
		//初始化visited陣列,用  false 以表示該頂點是否已經訪問過
		boolean[] visited = new boolean[gh.numVexs];
		for(int i = 0; i < gh.numVexs; i++){
			visited[i] = false;
		}
		System.out.println();
		for(int i = 0; i < gh.numVexs; i++){
			if(!visited[i])
				DFS(gh,i,visited);
		}
	}

	private void DFS(graph gh, int k,boolean[] vi) {
		// TODO Auto-generated method stub
	
		vi[k] = true;
		System.out.println("正訪問的結點是 : " + gh.vexs[k]);
		
		for(int i = 0; i < gh.numVexs; i++){
			if(gh.arc[k][i] == 1 && !vi[i])
				DFS(gh,i,vi);
		}
	}	
}


廣(寬)度優先遍歷(Breadth First Search):又稱為廣度優先搜尋,簡稱BFS 。 遍歷的過程是先從頂點 V0 出發,遍歷與 V0 直接連線而又未訪問過的頂點V1 、V2 、V3 等,再訪問 與 V1直接連線且未訪問過的頂點。同樣用一個數組來標記一個頂點是否已經訪問過,用一個佇列來儲存待訪問的頂點。 具體實現:
package com.hunter.Graph;

import java.util.LinkedList;
import java.util.Queue;

public class graphBFS {
	
	public void BFSraverse(graph gh){
		
		Queue<Integer> que =  new LinkedList<Integer>();
		boolean[] visited = new boolean[gh.numVexs];
		for(int i = 0; i < gh.numVexs; i++)
			visited[i] = false;
		
		for(int i = 0; i < gh.numVexs; i++){
			
			if(!visited[i]){
				
				visited[i] = true;
				System.out.println("正在訪問的節點是 :" + gh.vexs[i]);
				que.add(i);
				
				while(!que.isEmpty()){
					que.remove();
					
					for(int j = 0; j <gh.numVexs; j++){
						
						if(gh.arc[i][j] == 1 && !visited[j]){
							visited[j] = true;
							System.out.println("正在訪問的節點是 :" + gh.vexs[j]);
							que.add(j);
						}
					}
				}
			}
		}	
	}
}
深度優先和寬度優先的時間複雜度是一樣的,但是訪問的順序不一樣。 深度優先類似二叉樹的先序遍歷而寬度優先類似二叉樹的層序遍歷。

最後再看一個例子,有如下的無向圖:
初始化該圖,呼叫深度優先和寬度優先遍歷:
package com.hunter.Graph;

public class Demo {
	
	public static void main(String args[]){

		int numVexs = 7;
		int numEdges = 6;
		graph gh = new graph(numVexs,numEdges);
		gh.vexs = new char[numVexs];
		gh.vexs[0] = 'A';
		gh.vexs[1] = 'B';
		gh.vexs[2] = 'C';
		gh.vexs[3] = 'D';
		gh.vexs[4] = 'E';
		gh.vexs[5] = 'F';
		gh.vexs[6] = 'G';
		
		gh.arc = new int[numVexs][numVexs];
		gh.arc[0][1] = 1;
		gh.arc[1][0] = 1;
		gh.arc[0][4] = 1;
		gh.arc[4][0] = 1;
		gh.arc[1][2] = 1;
		gh.arc[2][1] = 1;
		gh.arc[2][3] = 1;
		gh.arc[3][2] = 1;
		gh.arc[3][4] = 1;
		gh.arc[4][3] = 1;
		gh.arc[2][5] = 1;
		gh.arc[5][2] = 1;
		gh.arc[1][5] = 1;
		gh.arc[5][1] = 1;
		gh.arc[6][3] = 1;
		gh.arc[3][6] = 1;			
		
		System.out.println("********************廣度優先搜尋********************");
		graphDFS ghDFS = new graphDFS();
		ghDFS.DFSraverse(gh);
		System.out.println("********************廣度優先搜尋********************");
		graphBFS ghBFS = new graphBFS();
		ghBFS.BFSraverse(gh);
	}
}


實際執行結果: