您的位置:首页 > 其它

算法导论学习笔记-第二十二章-图的基本算法

2010-08-20 00:10 435 查看
第二十二章
图的基本算法

总结:这一章讲了图的一些基本算法,例如广度优先搜索、深度优先搜索等,还介绍了拓扑排序、强连通分支等。

1.
图的表示
G=(V,E)
表示方法:邻接表、邻接矩阵
邻接表:存储空间O(V+E)
邻接矩阵:适合稠密图,存储空间O(V2)。还有一个优点,对邻接矩阵中的元素,可用二进位表示。

2.
广度优先搜索BFS
对图中的顶点,从源顶点出发,先访问源顶点可以到达的每个顶点,再对这些顶点,依次访问它们可以到达的每个顶点,这样依次一圈圈访问下去,按广度搜索下去。
对V中的每个顶点u,有color[u]代表它的访问状态(白色,未访问;灰色,发现并访问中;黑色,访问完),pi[u]代表它在生成广度优先树中的父母,d[u]代表u与源顶点s之间的最短距离。
总运行时间:O(V+E)

伪代码

BFS(G,s)

for each vetex uЄV(G)-{s}



do color[u] <- WHITE

pi[u] <- NIL

d[u] <- 无穷大
color[s] <- GRAY
d[s] <- 0
pi[s] <- NIL
Q <-
空队列
ENQUEUE(Q,s)
while Q!=空

do u <- DEQUEUE(Q)

for each vЄAdj[u]

do if color[v]=WHITE

then d[v] <- d[u]+1

pi[v] <- u

color[v] <- GRAY

ENQUEUE(Q,v)

color[u] <- BLACK

打印从s到v的最短路径。s到v的最短路径就是s到pi[v]的最短路径,加上pi[v]到v的最短路径。
伪代码

PRINT-PATH(G,s,v)

if v=s

then print s

else if pi[v]=NIL

then print “no path from ” s “ to ” v “ exists.”

else PRINT-PATH(G,s,pi[v])

print v

3.
深度优先搜索DFS
对最新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续探测下去。当顶点v的所有边都已被探测过后,探索将回溯到发现顶点v有起始点的那些边。这一过程一直进行到已发现从源顶点可达的所有顶点时为止。如果还存在未被发现的顶点,则选择其中一个作为源顶点,并重复以上过程。
对每个顶点u,color[u]代表访问状态(白色,未发现;灰色,被发现;黑色,结束时)。d[u]代表发现u的时间,f[u]代表完成对u的探索时的时间。
总运行时间:O(V+E)
伪代码

DFS(G)

for each vertex uЄV(G)

do color[u] <- WHITE

pi[u] <- NIL

time <- 0
for each vertex uЄV(G)
do if color[u]=WHITE
then DFS-VISIT(u)

DFS-VISIT(u)
time <- time+1
d[u] <- time
color[u] <- GRAY
for each vertex vЄAdj[u]

do if color[v]=WHITE

then pi[v] <- u
DFS-VISIT(v)
color[u] <- BLACK
time <- time+1
f[u] <- time

深度优先搜索的性质:
1.
它的先辈子图形成了一个由深度优先树组成的深度优先森林
2.
括号定理

根据由图生成的深度优先森林,可以把图的边分类:树边、反向边、正向边、交叉边

4.
拓扑排序
对有向无回路图G=(V,E)进行拓扑排序后,结果为该图所有顶点的一个线性序列,满足若G包含边(u,v),则在该序列中,u就出现在v的前面。一个图的拓扑排列可以看成是图中所有顶点沿水平线排列而成的一个序列,使得所有的有向边均从左指向右。
即将v按f[v]从大到小排列
总运行时间:O(V+E)

伪代码

TOPOLOGICAL(G)

call DFS(G) to compute f[v] for each vertex v
as each vertex if finished, insert it onto the front of a linked list
return the linked list of vertices

5.
强连通分支
强连通分支就是一个最大的顶点集合C,对与C中的每一对顶点u和v,u和v都是互相可达的。

算法思路:
DFS(G)计算出G中每个顶点u的f[u],计算GT,对GT调用DFS,且对源顶点的访问顺序根据f[u]的大小,从大到小的进行。最后,获得的深度优先森林中的各深度优先树,就是图的各个强连通分支。

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

//只是为了事先图的基本算法
//因此,很多对图的基本操作都简化了,并没有提供相应的接口。
//图采用邻接表表示法

typedef enum color{WHITE,GRAY,BLACK} col;

#define INF 100000000

class Vertex
{
public:
	Vertex(int k=0)
	{
		key=k;
		color=WHITE;
		pi=NULL;
	}
	int key;//顶点的序号
	vector<Vertex*> Adj;//邻接点
	col color;
	Vertex* pi;
	int d;//最短距离
	int dtime;//DFS中的时间戳
	int ftime;
};

class Graph
{
public:
	Graph(){vNum=0;}
	Vertex* V;//点集
	int vNum;
	void BFS(Vertex *s);
	void PRINT_PATH(Vertex *s,Vertex *v);
	void DFS();
	void DFS_visit(Vertex *u);
};

void Graph::BFS(Vertex* s)
{
	for(int i=0;i<vNum;i++)
	{
		V[i].color=WHITE;
		V[i].pi=NULL;
		V[i].d=INF;
	}
	queue<Vertex*> Q;
	s->color=GRAY;
	s->pi=NULL;
	s->d=0;
	Q.push(s);
	while(!Q.empty())
	{
		Vertex *u=Q.front();
		Q.pop();
		for(int i=0;i<u->Adj.size();i++)
		{
			if(u->Adj[i]->color==WHITE)
			{
				u->Adj[i]->color=GRAY;
				u->Adj[i]->pi=u;
				u->Adj[i]->d=u->d+1;
				Q.push(u->Adj[i]);
			}
		}
		u->color=BLACK;
		cout << u->key << " ";
	}
	cout << endl;
}

void Graph::PRINT_PATH(Vertex *s,Vertex *v)
{
	if(v==s)
		cout << s->key << " ";
	else
	{
		if(v->pi==NULL)
			cout << "no path from s to v" << endl;
		else
		{
			PRINT_PATH(s,v->pi);
			cout << v->key << " ";
		}
	}
}

int time;
void Graph::DFS()
{
	for(int i=0;i<vNum;i++)
	{
		V[i].color=WHITE;
		V[i].dtime=0;
		V[i].ftime=0;
		V[i].pi=NULL;
	}
	time=0;
	for(int i=0;i<vNum;i++)
	{
		if(V[i].color==WHITE)
		{
			cout << endl;
			cout << V[i].key << " ";
			DFS_visit(&V[i]);
		}
	}
}

vector<Vertex*> Link;
void Graph::DFS_visit(Vertex* v)
{
	v->color=GRAY;
	v->dtime=++time;
	for(int i=0;i<v->Adj.size();i++)
	{
		if(v->Adj[i]->color==WHITE)
		{
			cout << v->Adj[i]->key << " ";
			v->Adj[i]->pi=v;
			DFS_visit(v->Adj[i]);
		}
	}
	v->color=BLACK;
	v->ftime=++time;
	Link.push_back(v); //若要拓扑排序,则添加这一行
}

int main()
{
	Graph G;
	G.V=new Vertex[8];
	for(int i=0;i<8;i++)
		G.V[i].key=i+1;
	G.V[0].Adj.push_back(&G.V[1]);
	G.V[0].Adj.push_back(&G.V[7]);
	G.V[1].Adj.push_back(&G.V[2]);
	G.V[1].Adj.push_back(&G.V[7]);
	G.V[2].Adj.push_back(&G.V[5]);
	G.V[3].Adj.push_back(&G.V[4]);
	G.V[3].Adj.push_back(&G.V[2]);
	G.V[4].Adj.push_back(&G.V[5]);
	G.V[6].Adj.push_back(&G.V[7]);
	G.vNum=8;
	G.BFS(&G.V[0]);//输出从V[0]出发可达的顶点,并根据到V[0]的最短距离的顺序输
	G.PRINT_PATH(&G.V[0],&G.V[3]);
	G.PRINT_PATH(&G.V[0],&G.V[2]);
	G.DFS();
	cout << endl;
	for(int i=Link.size()-1;i>=0;i--)
		cout << Link[i]->key <<" " ; //拓扑排序后的顺序
	cout << endl;
	while(1);
	return 0;
}

//	样图,共8个节点,邻接表表示
//
//	1: 2 -> 8
//	2: 3 -> 8
//	3: 6
//	4: 5 -> 3
//	5: 6
//	6: null
//	7: 8
//	8: null
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: