您的位置:首页 > 其它

算法导论-第22章-基本的图算法-22.3 深度优先搜索(DFS)

2014-11-26 10:45 399 查看


一、综述

深搜策略:在深搜过程中,对于最新发现的顶点,如果它还有以此为起点的而未探测到的边,就沿此边继续探测下去。当顶点v的所有边都已被探测过后,搜索将回溯到发现顶点v有起始点的那些边。
时间戳:当顶点v第一次被发现时记录下第一个时间戳d[v],当结束检查v的邻接表时,记录下第二个时间戳f[v]。v在d[v]时刻前是白色的,在时刻d[v]和f[v]之间是灰色的,在时刻f[v]之后是黑色的。
括号定理,后裔区间的嵌套,白色路径定理
边的分类:(1)树边(2)反向边(3)正向边(4)交叉边
对无向图G进行深度搜索时,G的每一条边,要么是树边,要么是反向边。


二、代码


1.Link_Graph.h

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

#define N 100

#define WHITE 0
#define GRAY 1
#define BLACK 2

#define NONE 0
#define TREE 1
#define BACK 2
#define FORWARD 3
#define CROSS 4

struct Edge
{
int start;
int end;
int value;//边的权值
int type;//边的类型,初始值为NONE
Edge *next;
Edge(int s, int e, int v)
:start(s),end(e),value(v),type(NONE),next(NULL){}
};
struct Vertex
{
int d, f;//第一次被发现的时间和结束检查的时间
int color;//顶点的颜色
int p;//指向遍历结果的父结点
Edge *head;//指向以该顶点为起点的下一条边
Vertex():color(WHITE),p(0),head(NULL){};
};
class Link_Graph
{
public:
int time;
int n;
Vertex *V;
Link_Graph(int num):n(num)
{
V = new Vertex[n+1];
}
~Link_Graph(){delete []V;}
void AddSingleEdge(int start, int end, int value = 1)
{
Edge *NewEdge = new Edge(start, end, value);
//如果start点的链表为空,或者,链表第一个边的end比要加入的边的end大, 则把这条边作为第一条边(保证按end由小到大排序)
if(V[start].head == NULL || V[start].head->end > end)
{
NewEdge->next = V[start].head;
V[start].head = NewEdge;
}
else
{
Edge *e = V[start].head, *pre = e;
//查找要插入的位置,循环结束条件有三种情况,1、e->end = NewEdge->end  2、e->end < NewEdge->end 3、 e == NULL
while(e != NULL && e->end < end)
{
pre = e;
e = e->next;
}
if(e && e->end == end)
{
delete NewEdge;
return;
}
NewEdge->next = e;
pre->next = NewEdge;
}
}
void AddDoubleEdge(int a, int b, int value = 1)
{
AddSingleEdge(a, b, value);
AddSingleEdge(b, a, value);
}
void DeleteSingleEdge(int start, int end)
{
Edge *e = V[start].head, *pre = e;
while(e && e->end < end)
{
pre = e;
e = e->next;
}
if(e == NULL || e->end > end) return;
if(e == V[start].head)
V[start].head = e->next;
else
pre->next = e->next;
delete e;
}
void DeleteDoubleEdge(int a, int b)
{
DeleteSingleEdge(a, b);
DeleteSingleEdge(b, a);
}
//22.3
void DFS();
void DFS_Visit(int u);
void Print_Vertex_Time();
void Print_Edge_Type();
};

void Link_Graph::DFS()
{
int u;
//对每个顶点初始化
for(u = 1; u <= n; u++)
{
V[u].color = WHITE;
V[u].p =  NULL;
}
//时间戳初始化
time = 0;
//依次检索V中的顶点,发现白色顶点时,调用DFS_Visit访问该顶点
for(u = 1; u <= n; u++)
if(V[u].color == WHITE)
DFS_Visit(u);
}

void Link_Graph::DFS_Visit(int u)
{
int v;
Edge *e;
//将u置为灰色
V[u].color = GRAY;
//使全局变量time增值
time++;
//将time的新值记录为发现时间
V[u].d = time;
e = V[u].head;
while(e)
{
v = e->end;
//如果顶点为白色
if(V[v].color == WHITE)
{
//递归访问顶点
V[v].p = u;
DFS_Visit(v);
//树边
e->type = TREE;
}
else if(V[v].color == GRAY)
{
//反向边/后向边,v的颜色为灰色,说明v已经遍历过,因此v是e的祖先,所以是后向边
e->type = BACK;
}
else if(V[v].color == BLACK)
{
//正向边/前向边
if(V[u].d < V[v].d)
e->type = FORWARD;
//交叉边
else
e->type = CROSS;
}
e = e->next;
}
//以u为起点的所有边都被探寻后,置u为黑色
V[u].color = BLACK;
//并将完成时间记录在f[u]中
time++;
V[u].f = time;
}

void Link_Graph::Print_Vertex_Time()
{
int i;
for(i = 1; i <= n; i++)
{
//因为书中的例子中用从u开始的字母编号的,所以输出的时候有这样一个转换
cout<<char('t'+i)<<':';
cout<<V[i].d<<' '<<V[i].f<<endl;
}
}

void Link_Graph::Print_Edge_Type()
{
int i;
for(i = 1; i <= n; i++)
{
Edge *e = V[i].head;
while(e)
{
cout<<char(e->start+'t')<<"->"<<char(e->end+'t')<<": ";
switch (e->type)
{
case TREE:
cout<<"树边"<<endl;
break;
case BACK:
cout<<"反向边"<<endl;
break;
case FORWARD:
cout<<"正向边"<<endl;
break;
case CROSS:
cout<<"交叉边"<<endl;
break;
}
e = e->next;
}
}
}



2.main.cpp

#include <iostream>
using namespace std;

#include "Link_Graph.h"

int main()
{

//6个点,8条边
int graph[9][2]={{0,0},{1,2},{1,4},{2,5},{3,5},{3,6},{4,2},{5,4},{6,6}};
//构造一个空的图,含6个点
Link_Graph *G = new Link_Graph(6);
//输入边
for(int i = 1; i <= 8; i++)
{
//cin>>start>>end;
G->AddSingleEdge(graph[i][0], graph[i][1]);
}
//深度优先搜索
G->DFS();
//输出每个顶点的第一次访问时间和访问结束的时间
G->Print_Vertex_Time();
//输出每条边的类型
G->Print_Edge_Type();
delete G;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法导论