1. 程式人生 > >《演算法導論》第22章——基本的圖演算法

《演算法導論》第22章——基本的圖演算法

  雖然寫這個部落格主要目的是為了給我自己做一個思路記憶錄,但是如果你恰好點了進來,那麼先對你說一聲歡迎。我並不是什麼大觸,只是一個菜菜的學生,如果您發現了什麼錯誤或者您對於某些地方有更好的意見,非常歡迎您的斧正!

圖的搜尋指的是系統化地跟隨圖中的邊來訪問圖中的每個結點。

22.1圖的表示

在這裡插入圖片描述

權重圖:圖中的每條邊都帶有一個相關的權重的圖。
該權重值通常由一個w:E→R的權重函式給出。
例如,設G=(V,E)為一個權重圖,其權重函式為w,我們可以直接將邊(u,v)∈E的權重值w(u,v)存放在結點u的鄰接連結串列裡。

邊(u,v)表示結點u、v組成的一條邊。

鄰接連結串列的缺陷:無法快速判斷一條邊(u,v)是否在圖中。
辦法:在鄰接連結串列Adj[u]裡面搜尋結點v。

鄰接矩陣的表示:(如果結點1與結點5之間有邊,則(1,5)這個地方填入1)
在這裡插入圖片描述

鄰接矩陣表示權重的方法:把1替換成權重就可以了。

不論是有向圖還是無向圖,鄰接連結串列表示法的儲存空間需求均為θ(V+E)。
不管一個圖有多少條邊,鄰接矩陣的空間需求均為θ(V²)。
V是頂點數,E是頂點組成的邊的數量。

表示圖的屬性

v.d表示結點v的屬性d
(u,v).f表示邊(u,v)的屬性f

22.2廣度優先搜尋

廣度優先搜尋是最簡單的圖搜尋演算法之一。

廣度優先搜尋在概念上將每個結點塗上白色、灰色、黑色。
白色:沒有被發現
灰色:已知和未知的中間
黑色:所有與黑色結點鄰接的結點都已經被發現

屬性u.color:結點u的顏色
屬性u.π:結點u的前驅(如果u沒有前驅結點(沒被發現),u.π=NULL)
屬性u.d:計算出的從源結點s到結點u之間的距離
在這裡插入圖片描述

在這裡插入圖片描述

1、s入隊
佇列中:s
2、儲存s,s出隊,對s連結串列中(s->r->w)每個數進行判斷
佇列中:
3、r入隊,w入隊
佇列中:r->w
4、儲存w,w出隊,對w連結串列中(w->s->t->u)每個數進行判斷
佇列中:r
5、t入隊,x入隊
佇列中:r->t->x
6、儲存x,x出隊,對x連結串列中(x->w->t->u)每個數進行判斷
佇列中:r->t
7、u入隊,y入隊
佇列中:r->t->u->y
8、儲存y,y出隊,對y連結串列中(y->x–>u)每個數進行判斷
佇列中:r->t->u
9、沒有入隊
佇列中:r->t->u
10、然後依次u、t出隊,最後r的時候又檢索到一個v,最後全部出隊,迴圈結束

最短路徑

邊數最少,或者加權總數最小的路徑δ(s,v)。
如果最短路徑不存在,則為∞。記為δ(s,v)=∞
在這裡插入圖片描述在這裡插入圖片描述

廣度優先樹

列印從源結點s到結點v的一條最短路徑上的所有結點
在這裡插入圖片描述

22.3深度優先搜尋

深度優先搜搜總是對最近才發現的結點v的出發邊進行探索,直到該結點的所有出發邊都被發現為止。一旦結點v的所有出發邊都被發現,搜尋就“回溯”到v的前驅結點,來搜尋該前驅結點的出發邊。

沒發現一個結點v,將v的前驅屬性v.π設定為u。

深度優先搜尋的前驅子圖形成一個由多棵深度優先樹構成的深度優先森林,森林Eπ中的邊仍然稱為樹邊

深度優先搜尋演算法在每個結點上蓋一個時間戳。
第一個時間戳v.d:記錄結點v第一次被發現的時間(塗上灰色的時候)
第二個時間戳v.f:記錄搜尋樹完成對v的鄰接連結串列掃描的時間(塗上黑色的時候)
在這裡插入圖片描述
在這裡插入圖片描述

我的總結:
廣度搜索:每次找到一個點,就找到它的所有能達到的點
深度搜索:每次找到一個點,就一直沿著它能達到的點找下去,找不到了就返回換一個方向。

深度優先搜尋的性質

1、生成的前驅子圖Gπ是由多棵樹組成的森林
2、結點的發現時間和完成時間具有所謂的括號化性質

在這裡插入圖片描述

22.4拓撲排序

這一節闡述使用深度優先搜尋來對有向無環圖進行拓撲排序。
對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u線上性序列中出現在v之前。(摘自百度百科)
對於一個有向無環圖G=(V,E),其拓撲排序是G中所有結點的一種線性排序。
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

22.5強連通分量

強連通分量:u->v存在,v->u存在,就是可以相互到達。或者有u->v->w->v,這樣形成一個迴路,也是強聯通分量。簡單地說,就是可以構成一個環。

這一節真心看不懂
①為什麼兩次深度搜索後就可以得到解,
②為什麼要對圖進行轉置,
③做完這些後是怎麼得到解的?
希望有了解的大佬能夠科普我一下。感激不盡。

程式碼部分(建議貼上到編輯器自己執行一下):

廣度優先搜尋.h

#pragma once
#define VN 8/*點的數量*/

/*點的顏色*/
enum VerColor
{
	WHITE,	/*白色*/
	GRAY,	/*灰色*/
	BLACK	/*黑色*/
};
/*點*/
typedef struct Vertex
{
	char data;		/*點的名字*/
	VerColor color;	/*點的顏色*/
	Vertex* π;		/*點的前驅*/
	Vertex* Adj[VN];/*每個點連線的其它點*/
	int d;			/*源結點s到這個點的最短距離*/
}Vertex;

/*圖*/
typedef struct Graph
{
	Vertex* V[VN];
}Graph;

/*佇列*/
typedef struct Queue
{
	int tail;/*佇列中最後一個數據*/
	Vertex* Q[VN];/*佇列主體*/
}Queue;
/*判斷佇列是否為空*/
bool IsEmpty(Queue* &queue);
/*入隊*/
void EnQueue(Queue* &queue, Vertex* x);
/*出隊*/
Vertex* DeEnqueue(Queue* &queue);
/*廣度優先搜尋*/
void BFS(Graph* &G, Vertex* s);
/*廣度優先樹*/
void BFCpath(Vertex* s, Vertex* v);
/*測試函式*/
void TestBFS();

廣度優先搜尋.cpp

#include "廣度優先搜尋.h"
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

/*判斷佇列是否為空*/
bool IsEmpty(Queue* &queue)
{
	return queue->tail == 0;
}
/*入隊*/
void EnQueue(Queue* &queue,Vertex* x)
{
	queue->Q[queue->tail] = x;
	queue->tail++;/*佇列裡元素個數+1*/
}
/*出隊*/
Vertex* DeEnqueue(Queue* &queue)
{
	if (IsEmpty(queue))
		return NULL;
	else
	{
		queue->tail--;
		return queue->Q[queue->tail];
	}
}
/*廣度優先搜尋*/
void BFS(Graph* &G, Vertex* s)
{
	Vertex* u = new Vertex();
	Vertex* v = new Vertex();
	int i;

	for (i = 0; i < VN; i++)/*初始化*/
	{
		u = G->V[i];
		u->color = WHITE;
		u->d = 100;/*無窮大*/
		u->π = NULL;
	}

	s->color = GRAY;/*源結點初始化*/
	s->d = 0;
	s->π = NULL;

	Queue* q = new Queue();
	q->tail = 0;

	cout << s->data << "(" << s->d << ") ";
	EnQueue(q, s);
	while (!IsEmpty(q))
	{
		u = DeEnqueue(q);
		for (i = 0; i < VN; i++)
		{
			v = u->Adj[i];
			if (v&&v->color == WHITE)
			{
				v->color = GRAY;
				v->d = u->d + 1;
				v->π = u;
				cout << v->data <<"("<<v->d<< ") ";
				EnQueue(q, v);
			}
		}
		u->color = BLACK;
	}
}

/*廣度優先樹*/
void BFCpath(Vertex* s, Vertex* v)
{
	if (v == s)
		cout << s->data << " ";
	else if (v->π == NULL)
		cout << "No path" << endl;
	else
	{
		BFCpath(s, v->π);
		cout << v->data << " ";
	}
}
/*測試函式*/
void TestBFS()
{
	Vertex* s = new Vertex(); s->data = 's';
	Vertex* r = new Vertex(); r->data = 'r';
	Vertex* v = new Vertex(); v->data = 'v';
	Vertex* t = new Vertex(); t->data = 't';
	Vertex* u = new Vertex(); u->data = 'u';
	Vertex* w = new Vertex(); w->data = 'w';
	Vertex* x = new Vertex(); x->data = 'x';
	Vertex* y = new Vertex(); y->data = 'y';
	s->Adj[0] = r;s->Adj[1] = w;
	r->Adj[0] = s; r->Adj[1] = v;
	t->Adj[0] = w; t->Adj[1] = x; t->Adj[2] = u;
	u->Adj[0] = t; u->Adj[1] = x; u->Adj[2] = y;
	v->Adj[0] = r;
	w->Adj[0] = s; w->Adj[1] = t; w->Adj[2] = x;
	x->Adj[0] = w; x->Adj[1] = t; x->Adj[2] = u; x->Adj[3] = y;
	u->Adj[0] = x; y->Adj[1] = u;
	Graph* G = new Graph();
	G->V[0] = s;
	G->V[1] = r;
	G->V[2] = t;
	G->V[3] = u;
	G->V[4] = v;
	G->V[5] = w;
	G->V[6] = x;
	G->V[7] = y;

	BFS(G, s);

	cout << endl;
	cout << endl;
	cout << "列印從s到y的路徑" << endl;
	BFCpath(s, y);
}

主函式

#include "廣度優先搜尋.h"
#include <stdio.h>

int main()
{
	TestBFS();
	getchar();
	getchar();
	return 0;
}

執行結果
在這裡插入圖片描述
深度優先搜尋.h

#pragma once
#define DFCN 6/*頂點的個數*/

/*顏色*/
enum DFC_COLOR
{
	DWHITE,
	DGRAY,
	DBLACK
};
/*頂點*/
typedef struct DFCV
{
	char data;/*資料*/
	int d;/*第一次被訪問的時候*/
	int f;/*記錄鄰接結點被訪問完成的時候*/
	DFC_COLOR color;/*顏色*/
	DFCV* π;/*前驅結點*/
	DFCV* next[3];/*記錄它能到達的點*/
}DFCv;

typedef struct DFCG
{
	DFCv* V[DFCN];/*記錄每個頂點*/
}DFCg;

/*深度優先搜尋*/
void DFC(DFCg* &G);
/*遍歷函式*/
void DfcVisit(DFCg* &G, DFCv* u);

/*列印時間戳*/
void DFCprint(DFCg* &G);

/*測試函式*/
void TestDFC();

深度優先搜尋.cpp

#include "深度優先搜尋.h"
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

int time = 0;/*time應該設為全域性變數!注意!*/

/*深度優先搜尋*/
void DFC(DFCg* &G)
{
	int i = 0;
	//int time;
	DFCv* u = new DFCv();

	for (i = 0; i < DFCN; i++)/*初始化過程*/
	{
		u = G->V[i];
		u->color = DWHITE;
		u->π = NULL;
	}
	
	time = 0;
	
	for (i = 0; i < DFCN; i++)/*對每個結點進行遍歷*/
	{
		u = G->V[i];
		if (u->color == DWHITE)
			DfcVisit(G, u);
	}
}
/*遍歷函式*/
void DfcVisit(DFCg* &G, DFCv* u)
{
	int i;
	DFCv* v = new DFCv();

	time++;
	u->d = time;
	u->color = DGRAY;

	for (i = 0; i < 3; i++)
	{
		v = u->next[i];/*v是u可以直接達到的點*/
		if (v&&v->color == DWHITE)/*如果v存在且v的顏色為白色*/
		{
			v->π = u;/*設定v的前驅*/
			DfcVisit(G, v);/*對v繼續深入搜尋*/
		}
	}
	u->color = DBLACK;/*此時已經搜尋完u的鄰接連結串列了,所以置為黑色*/
	time++;
	u->f = time;/*結束時間*/
}
/*列印時間戳*/
void DFCprint(DFCg* &G)
{
	for (int i = 0; i < DFCN; i++)
	{
		cout << G->V[i]->data << "  (" << G->V[i]->d << "/" << G->V[i]->f << ")" << endl;
	}
	cout << endl;
}
/*測試函式*/
void TestDFC()
{
	DFCv* u = new DFCv(); u->data = 'u';
	DFCv* v = new DFCv(); v->data = 'v';
	DFCv* w = new DFCv(); w->data = 'w';
	DFCv* x = new DFCv(); x->data = 'x';
	DFCv* y = new DFCv(); y->data = 'y';
	DFCv* z = new DFCv(); z->data = 'z';
	u->next[0] = v; u->next[1] = x;
	v->next[0] = y;
	w->next[0] = y; w->next[1] = z;
	x->next[0] = v;
	y->next[0] = x;
	z->next[0] = z;

	DFCg* G = new DFCg();
	G->V[0] = u;
	G->V[1] = v;
	G->V[2] = w;
	G->V[3] = x;
	G->V[4] = y;
	G->V[5] = z;

	DFC(G);
	DFCprint(G);
}

主函式

#include "深度優先搜尋.h"
#include <stdio.h>

int main()
{
	TestDFC();
	getchar();
	getchar();
	return 0;
}

執行結果

在這裡插入圖片描述