1. 程式人生 > >PAT 備考第一天——圖論演算法(一)

PAT 備考第一天——圖論演算法(一)

大綱:

必考考點:
1.圖的定義和相關術語
2.圖的儲存(鄰接矩陣和鄰接表)
3.圖的遍歷(DFS和BFD)
4.最短路徑演算法
5.拓撲排序

非重點考點:
1.關鍵路徑
2.最短路徑中的Bellman-Ford和SPFA

甲級考綱以外的考點:
最小生成樹演算法

一、圖的定義和相關術語
只列出術語:頂點(Vertex)、邊(Edge)、權值、有向圖、無向圖、頂點的度、出度、入度、點權和邊權

二、圖的儲存
1.鄰接矩陣:Gij表示i點到j點的連線權值,因此無向圖鄰接矩陣對稱
2.鄰接表:連結串列的表頭構成的陣列,每一個連結串列表示與表頭所對應頂點所有相連線的點

三、圖的遍歷


1.深度優先搜尋(DFS, Depth First Searching)
1.1.遍歷概述:沿著一條路徑前進,直到無法繼續前進,才退回到路徑上距離當前頂點最近的還存在未訪問分支頂點的岔道口,並前往訪問那些未訪問過的頂點,直到遍歷整個圖。
1.2.虛擬碼:

DFS(u){
	vis[u]=true;
	for(從u出發能到達的所有頂點v){
		if(vis[v]==false) DFS(v);
	}
}
DFSTraversal(G){
	for(G中所有頂點u){
		if(vis[u]==false) DFS(u);
	}
}

1.3. 鄰接矩陣版程式碼與鄰接表版程式碼

constint MAXV = 1000;
constint INF = 1000000000; //設定一個很大的數

鄰接矩陣版

int n, G[MAXV][MAXV];
bool vis[MAXV] = {false};

void DFS(int u, int depth){
	vis[u] = true;
	for(int v=0; v<n; v++){
		if(G[u][v] != 0 && vis[v]==false)
			DFS(v, depth + 1);
	}
}

void DFSTraversal(){
	for(int u=0; u<n; u++){
		if(vis[u]==false) DFS(u,1);//1表示第一層
	}
}

鄰接表版

//無權圖
vector<int> Adj[MAXV];
//有權圖: vector<Node> Adj[MAXV];
//struct Node{
//	int v;
//	int w;///權重
//	Node(int _v, _w) : v(_v), w(_w) {}		
//	此為建構函式,插入邊程式碼可直接寫作Adj[x].push_pack(Node(3,4));//插入頂點為3權重為4的一條邊
//};
int n;
bool vis[MAXV]={false};
void DFS(int u, int depth){
	vis[u] = true;
	/*如果需要對u進行操作,就在此處插入*/
	for(int i=0; i<Adj[u].Size(); i++){//相比鄰接矩陣少了非零判斷
		if(vis[v]==false) DFS(v, depth+1);
	}
}
void DFSTraversal(){//與鄰接矩陣沒有區別
	for(int u=0; u<n; u++){
		if(vis[u]==false) DFS(u, 1);	
	}
}

2.廣度優先搜尋(BFS,Breadth First Search)
2.1.遍歷概述

2.2.虛擬碼

BFS(u){
	queue q;
	q.enqueue(u);//入隊
	inq[u]=true;//使u入隊並標記
	while(q非空){
		取出q佇列頭m進行訪問;
		for(m所有連線點v){
			if(inq[v]==false) {
				q.enqueue(v);
				inq[v]=true;
			}
		}
	}
}

BFSTraversal(){
	for(G的所有頂點u){
		if(inq[u]==false){
			BFS(u);
		}
	}
}

2.3.鄰接矩陣版程式碼與鄰接表版程式碼

const int MAXV=1000;
const int INF=1000000000;

鄰接矩陣

int G[MAXV][MAXV];//定義鄰接矩陣圖
int n;//頂點數
bool inq[MAXV]={false};
void BFS(int u){
	queue<int> q;
	q.push(u);//入隊並標記
	inq[u]=true;
	while(!q.empty()){//迴圈結束條件只有佇列q為空
		int m=q.front();//取出佇列頭
		q.pop();
		for(int i=0; i<n; i++){//遍歷每一行
			if(inq[i]==false && G[m][i]!=0){
				q.push(i);//入隊並標記
				inq[i]=true;
			}
		}
	}
}
//遍歷圖中所有點
void BFSTraversal(){
	for(int i=0; i<n; i++){
		//沒有被遍歷過則BFS
		if(inq[i]==false) BFS(i);
	}
}

鄰接表

vector<int> Adj[MAXV];//無權圖
//vector<Node> Adj[MAXV];有權圖這樣表示
//struct Node{
//	int data;
//	int weight;
//	Node(int _data, int _weight) : data(_data), weight(_weight) {}//這樣方便賦值,Node(1,2)表示點值為1,權重為2
//};
int n;//個數
bool inq[MAXV]={false};//訪問情況

void BFS(int u){
	queue<int> q;
	q.push(u);
	inq[u]=true;
	while(!q.empty()){
		int m=q.front();
		q.pop();
		//區別在這裡,注意小於的是Adj[m].size,需要考慮這是什麼形狀的鄰接表??
		for(int i=0; i<Adj[m].size(); i++){
			int v = Adj[m][i];
			if(inq[v] ==false){
				q.push(v);
				inq[v]=true;
			}
		}
	}
}
//同鄰接矩陣
void BFSTraversal(){
	for(int i=0; i<n; i++){
		//沒有被遍歷過則BFS
		if(inq[i]==false) BFS(i);
	}
}