1. 程式人生 > >其它 >圖遍歷詳解(C語言版)

圖遍歷詳解(C語言版)



一、定義

從給定圖中任意指定的頂點(稱為初始點)出發,按照某種搜尋方法沿著圖的邊訪問圖中所有頂點,使每個頂點僅被訪問一次,這個過程稱為圖的遍歷。如果給定圖是連通的無向圖或者是強連通的有向圖,則遍歷過程一次就能完成,並可按訪問的先後順序得到由該圖的所有頂點組成的一個序列。

二、方法

在遍歷的過程中,從圖的初始點到達圖中的每個頂點可能存在多條路徑。當沿著圖中的一條路徑訪問過某頂點之後,可能還沿著另一條路徑回到該頂點,即存在迴路。為了避免同一個頂點被重複訪問,需要設定一個訪問標記陣列visited,當頂點i,被訪問過時,陣列中的元素visited[i]置為1,否則置為0。
圖的遍歷過程實際就是搜尋過程,根據搜尋方法的不同,圖的遍歷方法有兩種:一種是深度優先遍歷,另一種叫廣度優先遍歷。

1、深度優先遍歷

深度優先遍歷的過程是從圖中的某個初始點v出發,首先訪問初始點v,然後選擇一個與頂點v相鄰且沒被訪問過的頂點w,以w為初始頂點,再從它出發進行深度優先遍歷,直到圖中與頂點v鄰接的所有頂點都被訪問為止,顯然這是一個遞迴過程。
下面給出一個示例:

圖遍歷詳解(C語言版)


該圖以A為初始點,從左往右通過深度優先遍歷可以得到序列:ABDHECFG ,當然如果採取從右往左通過深度優先遍歷可以得到序列:ACGFBEHD,可見採用深度優先遍歷得到的序列並不是唯一的,這與建圖時的儲存有關,假如採用鄰接表來儲存,當每個頂點的鄰接頂點採用頭插法插入,那麼之後採用深度優先遍歷進行遍歷時就會得到序列ACGFBEHD,當每個頂點的鄰接頂點採用尾插法插入,那麼之後採用深度優先遍歷進行遍歷就得到序列ABDHECFG。
下面來看看深度優先遍歷的程式碼

//深度優先遍歷:  g:圖  v:初始點    visited:標記陣列
void DFS(GraphLnk *g, int v, bool visited[])
{
	//訪問頂點,獲取值,列印
	printf("%c-->",GetVertexValue(g,v));
	visited[v] = true;//為訪問過的頂點做上標記
	int w = GetFirstNeighbor(g,GetVertexValue(g,v));//獲取第一個鄰接頂點
	while(w != -1)
	{//當鄰接頂點未被訪問
		if(!visited[w])
		{
			DFS(g,w,visited);//以該頂點為起始頂點進行深度優先遍歷
		}
		/*
		對第一個鄰接頂點深度優先遍歷完成,獲取下一個鄰接頂點的位置
		(如果該頂點未被訪問,那麼下一輪將以該頂點為起始頂點進行深度優先遍歷)
		*/
		w = GetNextNeighbor(g,GetVertexValue(g,v),GetVertexValue(g,w));
	}
}

2、廣度優先遍歷

廣度優先遍歷的過程是首先訪問初始點v,接著訪問頂點v的所有未被訪問過的鄰接點v1,v2,v3,…,vt,然後再按照v1,v2,v3,…,vt的次序訪問每一個頂點的所有未被訪問過的鄰接點,依此類推,直到圖中所有和初始點v有路徑相通的頂點都被訪問過為止。為了實現紅色部分描述的先訪問頂點的鄰接頂點先訪問,需要借用佇列來實現。
下面給出一個示例:

圖遍歷詳解(C語言版)


該圖以A為初始點,從左往右通過廣度優先遍歷可以得到序列:ABCDEFGH ,當然如果採取從右往左通過廣度優先遍歷可以得到序列:ACBGFEDH,可見採用廣度優先遍歷得到的序列也並不是唯一的,原因同深度優先遍歷不唯一的原因,此處不再贅述。
下面來看看廣度優先遍歷的程式碼

//廣度優先遍歷:g:圖  v:初始點  visited:標記陣列
void BFS(GraphLnk *g, int v, bool visited[])
{
	printf("%c-->",GetVertexValue(g,v));
	visited[v] = true;
	
	LinkQueue Q;//建立鏈隊
	InitQueue(&Q);//初始化

	EnQueue(&Q,v);//將起始頂點入隊
	int w;
	while(!Empty(&Q))//當隊內還有頂點
	{
		GetHead(&Q,&v);//獲取存在在隊頭的頂點v
		DeQueue(&Q);//出隊
		//獲取第一個鄰接頂點
		w = GetFirstNeighbor(g,GetVertexValue(g,v));
		while(w != -1)//存在鄰接頂點
		{
			if(!visited[w])//當該鄰接頂點未被訪問
			{
				//列印值
				printf("%c-->",GetVertexValue(g,w));
				visited[w] = true;//標記為已被訪問
				EnQueue(&Q,w);//將該頂點入隊,為之後訪問該頂點的鄰接頂點做鋪墊
			}
			//獲取頂點v的下一個鄰接頂點(該鄰接頂點的順序在前面訪問過鄰接頂點w之後)
			w = GetNextNeighbor(g,GetVertexValue(g,v),GetVertexValue(g,w));
		}
	}
}

三、實現

有了上面的理論基礎,下面將基於圖的鄰接表儲存來實現圖的遍歷,如果大家對圖的鄰接表儲存方式還不太理解,那麼可以先閱讀圖之鄰接表詳解(C語言版)此篇文章之後,再接著往下閱讀。

1、無向圖或強連通有向圖遍歷

如果給定圖是連通的無向圖或者是強連通的有向圖,則遍歷過程一次就能完成,並可按訪問的先後順序得到由該圖的所有頂點組成的一個序列。

深度優先遍歷實現

//深度優先遍歷:給出遍歷的圖g和起始頂點vertex
void DFS(GraphLnk *g, T vertex)
{
	int n = g->NumVertices;//獲取頂點個數
	//根據頂點數建立輔助陣列空間:用來標記哪些頂點已經訪問過,哪些頂點沒有訪問
	bool *visited = (bool*)malloc(sizeof(bool) * n);
	assert(visited != NULL);
	for(int i=0; i<n; ++i)//初始化標記陣列
	{
		visited[i] = false;
	}
	//獲取起始節點在鄰接表中的位置
	int v = GetVertexPos(g,vertex);
	DFS(g,v,visited);//遍歷
	free(visited);//釋放輔助空間
}

廣度優先遍歷實現

//廣度優先遍歷:給出遍歷的圖g和起始頂點vertex
void BFS(GraphLnk *g, T vertex)
{
	int n = g->NumVertices;//獲取頂點個數
	//根據頂點數建立輔助陣列空間:用來標記哪些頂點已經訪問過,哪些頂點沒有訪問
	bool *visited = (bool*)malloc(sizeof(bool) * n);
	assert(visited != NULL);
	for(int i=0; i<n; ++i)//初始化標記陣列
	{
		visited[i] = false;
	}
	//獲取起始頂點在鄰接表中的位置
	int v = GetVertexPos(g,vertex);
	BFS(g,v,visited);//遍歷	
	free(visited);
}

2、非連通圖遍歷

如果給定圖是非連通圖,則只能訪問到初始點所在分量中的所有頂點,其他連通分量中的頂點是不可能訪問到的,為此需要從其他每個連通分量中選擇初始點,分別進行遍歷,這樣才能夠訪問到圖中的所有頂點。

深度優先遍歷實現

//非連通圖的深度優先遍歷方式
void NonUnicomDFS(GraphLnk *g)
{
	int n = g->NumVertices;//獲取頂點個數
	//根據頂點數建立輔助陣列空間:用來標記哪些頂點已經訪問過,哪些頂點沒有訪問
	bool *visited = (bool*)malloc(sizeof(bool) * n);
	assert(visited != NULL);
	for(int i=0; i<n; ++i)//初始化標記陣列
	{
		visited[i] = false;
	}
	for(i=0; i<n; ++i) //遍歷非連通圖的頂點
	{
		if(!visited[i])//以沒有訪問的頂點為起始頂點進行深度優先遍歷
			DFS(g,i,visited);
	}
	free(visited);
}

廣度優先遍歷實現

//非連通圖的廣度優先遍歷方式
void NonUnicomBFS(GraphLnk *g)
{
	int n = g->NumVertices;//獲取頂點個數
	//根據頂點數建立輔助陣列空間:用來標記哪些頂點已經訪問過,哪些頂點沒有訪問
	bool *visited = (bool*)malloc(sizeof(bool) * n);
	assert(visited != NULL);
	for(int i=0; i<n; ++i)//初始化標記陣列
	{
		visited[i] = false;
	}
	for(i=0; i<n; ++i) //遍歷非連通圖的頂點
	{
		if(!visited[i])//以沒有訪問的頂點為起始頂點進行廣度優先遍歷
			BFS(g,i,visited);
	}
	free(visited);
}

結語

對圖遍歷的介紹就到這裡啦,希望這篇文章能給予你一些幫助,感謝各位人才的:點贊、收藏和評論,我們下次見。

附錄

測試程式碼:圖遍歷詳解(C語言版)