1. 程式人生 > >圖論演算法總結之二:遍歷圖

圖論演算法總結之二:遍歷圖

二、遍歷圖

1.bfs

(1)鄰接矩陣的情況

有幾個關鍵點:

①使用佇列保證了層數淺的節點永遠在層數深的節點之前出隊,這樣就不會出現一個淺層節點的相鄰邊還未遍歷就去遍歷一個深層節點的相鄰邊

②visited陣列是記錄已經入隊的節點的,避免同一層的節點多次訪問同一個下一層節點

int visited[num+10];
int Graph[num][num]={
	{1,0,1},
	{0,1,0},
	{1,0,1}
};
queue<int> q;
void bfs(int start){
	q.push(start);
	visited[start]=1;
	while(!q.empty()){
		int vertax=q.front();
		q.pop();
		/*
		判斷隊頭的屬性,對於搜尋的問題,滿足條件時退迴圈 
		*/
		for(int i=1;i<=num;i++){
			if(i!=vertax&&Graph[vertax-1][i-1]!=0&&visited[i]==0){
				q.push(i);
				visited[i]=1;	
			} 
		} 
	}
}
int main(){
    memset(visited,0,sizeof(visited)); 
    for(int i=1;i<=N;i++){//此迴圈可以用來計算有幾個圖 
        if(visited[i]==1)continue;
        bfs(i);
    }
    return 0;
} 

(2)鄰接表的情況

也有帶權值與不帶權值的區別,但是如果是把節點寫為結構體的話,似乎很複雜,這裡先放一放

#include<queue>
vector<int> G[num+10];
int visited[num+10];
queue<int> q;
void bfs(int start){
	q.push(start);
	visited[start]=1;
	while(!q.empty()){
		int vertax=q.front();
		q.pop();
		/*
		判斷條件,滿足則退迴圈 
		*/
		for(int i=0;i<G[vertax].size();i++){
			int point=G[vertax][i];
			if(visited[point]==0){
				visited[point]=1;
				q.push(point);
			}
		} 
	}
}
int main(){
	memset(visited,0,sizeof(visited));
	for(int i=1;i<=num;i++){
		if(visited[i]!=0)continue;
		bfs(i);
	}
	return 0;
}

2.dfs

(1)遞迴寫法:

這裡只寫一下鄰接表的情況,其實鄰接表與鄰接矩陣的最大不同在於鄰接矩陣需要先判斷兩個點之間是否有邊,而鄰接表直接判斷訪問過沒有就可以了

vector<int> G[num+10];
int visited[num+10];
void dfs(int start){
	visited[start]=1;
	for(int i=0;i<G[start].size();i++){
		int vertax=G[start][i];
		if(visited[vertax]==0){
			dfs(vertax);
		}
	}
}
int main(){
	memset(visited,0,sizeof(visited));
	for(int i=1;i<=num;i++){
		if(visited[i]!=0)continue;
		dfs(i);
	}
	return 0;
}

(2)不遞迴的寫法:

利用棧stack,關鍵點:

①is_push用來判斷是否已經無路可走,當前點凡不能擴充套件則應出棧

②使用stack,原因是要持續往前走,也就是下一次判斷的時候,當前點應該轉移到下一個點上,利用棧的特點,我們每一次看棧頭的時候,看到的都是最新的點,可以滿足需求

③由於所有的節點都會從出棧口經過,所以所有的判斷語句寫在這裡是比較方便的,當然,寫在所有的進棧口也不是不行,這一點可以類比queue實現bfs,bfs中的進隊口和出隊口都可以寫判斷語句,但是,不論是dfs還是bfs,都是在出棧口寫比較方便

#include<stack>
vector<int> G[num+10];
int visited[num+10];
stack<int> s; 
void dfs(int start){
	s.push(start);
	visited[start]=1;
	while(!s.empty()){
		int vertax=s.top();
		bool is_push=false;
		for(int i=0;i<G[vertax].size();i++){
			int point=G[vertax][i];
			if(visited[point]==0){
				is_push=true;
				s.push(point);
				visited[point]=1;
				break;
			}
		}
		if(!is_push){
			/*
			所有節點的出棧口,在這裡進行性質判斷比較好,但這是一個逆序 
			*/
			s.pop();
		}
	}
}
int main(){
	memset(visited,0,sizeof(visited));
	for(int i=1;i<=num;i++){
		if(visited[i]!=0)continue;
		dfs(i);
	}
	return 0;
}

3.時間複雜度

鄰接表的時間複雜度:O(|V|+|E|)

鄰接矩陣的複雜度:O(|V|^2)