By: 潘云登
Date: 2009-7-28
Email: intrepyd@gmail.com
Homepage: http://blog.csdn.net/intrepyd
Copyright: 该文章版权由潘云登所有。可在非商业目的下任意传播和复制。
对于商业目的下对本文的任何行为需经作者同意。
写在前面
1. 本文内容对应《算法导论》(第2版)》第22章。
2. 主要介绍了图的两种表示方式(邻接表和邻接矩阵),以及广度优先搜索和深度优先搜索。
3. 希望本文对您有所帮助,也欢迎您给我提意见和建议。
图的表示
要表示一个图G=(V, E),有两种标准的方法,即邻接表和邻接矩阵。这两种表示法既可以用于有向图,也可以用于无向图(它们可以是加权图)。通常采用邻接表表示法,因为这种方法表示稀疏图(即|E|远小于|V|的平方)比较紧凑。但是,当遇到稠密图或必须很快判别两个给定顶点是否存在连接边时,通常采用邻接矩阵表示法。
² 邻接表
图G=(V, E)的邻接表表示由一个包含|V|个列表的数组所组成,其中每个列表对应于V中的一个顶点。对于每一个u∈V,邻接表包含图G中所有和顶点u相邻的顶点(或者,它也可能包含指向这些顶点的指针)。对于加权图,可以将边的权值连同边一起存储在顶点的邻接表中。不论是有向图还是无向图,邻接表表示法都有一个很好的特性,即它所需要的存储空间为Θ(V+E)。不足在于,如果要确定图中边(u,v)是否存在,只能在顶点u的邻接表中搜索v。
² 邻接矩阵
在图G=(V, E)的邻接矩阵表示法中,假定各顶点按某种任意的方式编号为1, 2, …, |V|,那么G的邻接矩阵为一个|V|*|V|的矩阵A=(aij),它满足:如果(i, j)∈E,aij=1;否则,aij=0。对于加权图,(i, j)边的权值可以简单地存储在aij中。一个图的邻接矩阵表示需要占用Θ(V2)的存储空间,而与图中的边数多少无关。
广度优先搜索
在给定图G=(V, E)和一个特定的源顶点s的情况下,广度优先搜索(BFS,breadth first search)首先会发现和s距离为k的所有顶点,然后才发现和s距离为k+1的其它顶点。该搜索算法能够生产一棵根为s、且包括所有s的可达顶点的广度优先树。对从s可达的任意顶点v,广度优先树中从s到v的路径对应于图G中从s到v的一条最短路径。如果u处于树中从根s到顶点v的路径中,那么u成为v的祖先,v是u的后裔。BFS的总运行时间为O (V+E)。
为了记录搜索的轨迹,算法开始时,所有顶点都着成白色,随着搜索的进行,各顶点逐渐变成灰色,然后成为黑色。与黑色顶点相邻的所有顶点都是已经被发现的。灰色顶点可能会有一些白色的相邻顶点,它们代表了已发现和未发现顶点之间的边界。
深度优先搜索
在深度优先搜索(DFS,depth first search)中,对于新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续探测下去,当顶点v的所有边都已被探寻后,搜索将回溯到发现顶点v有起始点的那些边。DFS的总运行时间也是O (V+E)。
在深度优先搜索的同时,可以为每个顶点加盖时间戳。每个顶点v有两个时间戳:当顶点v第一次被发现并置为灰色时,记录下第一个时间戳d[v],当结束检查v的邻接表并置v为黑色时,记录下第二个时间戳f[v]。这样,在对一个有向或无向图G的任何深度优先搜索中,对于图中任意两个顶点u和v,下述三个条件中仅有一个成立:1)区间[d[u],f[u]]和区间[d[v],f[v]]是完全不相交的,且u或v都不是对方的后裔;2)区间[d[u],f[u]]完全包含于区间[d[v],f[v]]中,且u是v的后裔;3)区间[d[v],f[v]] 完全包含于区间区间[d[u],f[u]]中,且v是u的后裔。
利用深度优先搜索,可以对一个有向无回路图进行拓扑排序,即各个顶点按照搜索完成时间f进行降序排列。其结果为该图所有顶点的一个线性序列,满足如果G包含边(u,v),则在该序列中,u就出现在v的前面。在很多应用中,拓扑排序用于说明事件发生的先后顺序。
附邻接表表示的图算法(
可点此下载示例) #define WHITE 0 #define GRAY 1 #define BLACK 2
typedef struct link_node /*邻接表中的结点*/ { int index; int weight; /*边的权值*/ struct link_node *next; } lnode;
typedef struct vertex_node /*实际顶点*/ { int data; int color; int index; lnode *link; } vnode;
typedef struct graphic_queue { int size; int head; int tail; void **array; } queue;
typedef struct adjacency_list /*邻接表表示的图*/ { int vertex_number; /*顶点数*/ int edge_number; /*边数*/ vnode *vlist; /*存储所有顶点*/ int *dtime; /*搜索开始时间戳*/ int *ftime; /*搜索结束时间戳*/ int *ancestor; /*祖先顶点序号*/ queue bfs_queue; /*用于BFS的队列*/ int time; /*用于计算时间戳*/ } adjlist;
void new_graphic(adjlist *graphic, int vtx_number) { int i;
graphic->vertex_number = vtx_number; graphic->edge_number = 0; graphic->vlist = malloc(sizeof(vnode)*vtx_number); for(i=0; i<vtx_number; i++) { graphic->vlist[i].data = 0; graphic->vlist[i].color = WHITE; graphic->vlist[i].index = i; graphic->vlist[i].link = NULL; } graphic->dtime = malloc(sizeof(int)*vtx_number); graphic->ftime = malloc(sizeof(int)*vtx_number); graphic->ancestor = malloc(sizeof(int)*vtx_number); graphic->bfs_queue.size = vtx_number; graphic->bfs_queue.array = malloc(sizeof(void *)*vtx_number); graphic->bfs_queue.head = 0; graphic->bfs_queue.tail = 0; }
void free_graphic(adjlist *graphic); void set_vnode(adjlist *graphic, int index, int value);
void insert_edge(adjlist *graphic, int i, int j, int weight) { lnode *new_lnode=NULL;
new_lnode = malloc(sizeof(lnode)); new_lnode->index = j; new_lnode->weight = weight; new_lnode->next = graphic->vlist[i].link; graphic->vlist[i].link = new_lnode; graphic->edge_number++; }
void delete_edge(adjlist *graphic, int i, int j); void print_graphic(adjlist *graphic); void enqueue(queue *q, void *object); void * dequeue(queue *q); int is_queue_empty(queue *q);
void breadth_first_search(adjlist *graphic, int start) { int i; vnode *ancestor_vnode=NULL, *child_vnode=NULL; lnode *temp_lnode=NULL;
for(i=0; i<graphic->vertex_number; i++) { graphic->vlist[i].color = WHITE; graphic->dtime[i] = -1; graphic->ancestor[i] = -1; }
graphic->vlist[start].color = GRAY; graphic->dtime[start] = 0; enqueue(&graphic->bfs_queue, (void *)&graphic->vlist[start]); while(!is_queue_empty(&graphic->bfs_queue)) { ancestor_vnode = (vnode *)dequeue(&graphic->bfs_queue); temp_lnode = ancestor_vnode->link; while(temp_lnode != NULL) { child_vnode = &graphic->vlist[temp_lnode->index]; if(child_vnode->color == WHITE) { child_vnode->color = GRAY; graphic->ancestor[child_vnode->index] = ancestor_vnode->index; graphic->dtime[child_vnode->index] = graphic->dtime[ancestor_vnode->index]+1; enqueue(&graphic->bfs_queue, (void *)child_vnode); } temp_lnode = temp_lnode->next; } ancestor_vnode->color = BLACK; } }
void depth_first_search(adjlist *graphic) { int i;
for(i=0; i<graphic->vertex_number; i++) { graphic->vlist[i].color = WHITE; graphic->ancestor[i] = -1; }
graphic->time = 0; for(i=0; i<graphic->vertex_number; i++) { if(graphic->vlist[i].color == WHITE) dfs_visit(graphic, &graphic->vlist[i]); } }
void dfs_visit(adjlist *graphic, vnode *v) { lnode *temp_lnode=NULL;
v->color = GRAY; graphic->time++; graphic->dtime[v->index] = graphic->time; temp_lnode = v->link; while(temp_lnode != NULL) { if(graphic->vlist[temp_lnode->index].color == WHITE) { graphic->ancestor[temp_lnode->index] = v->index; dfs_visit(graphic, &graphic->vlist[temp_lnode->index]); } temp_lnode = temp_lnode->next; } v->color = BLACK; graphic->time++; graphic->ftime[v->index] = graphic->time; }
void print_bfs(adjlist *graphic); void print_dfs(adjlist *graphic); |