zoukankan      html  css  js  c++  java
  • 图论——读书笔记 (深度优先搜索)

    深度优先搜索是这样的,对最近才发现的结点v的所有出发边进行探索,

    直到该结点的所有出发边都被发现为止。

    例如上面的图,如果是按照以S点为源结点进行深度优先 遍历的话,

    相应的访问顺序是:

    (根据具体的应用不同,假定是以左访问优先,

    而所谓的左优先指的具体是:如上图,当访问到Z结点的时候,

    左优先,继续深入访问Y,而把W结点进行暂存起来)

    S->Z(W相对于Z靠右,Z被压栈)

    Z->Y

    Y->X

    X结点没有出发边,(所谓出发边指的是从当前结点出发指向其他结点的有向边)

    故回溯到Y,Y除了访问过的X同样没有出发边;

    回溯到Z,Z还有除了Y没被访问过的出发边指向被压进栈的W

    故W从栈中弹出,访问W。

    W没有出发边,故回溯到Z,Z再回溯到S。

    以上描述就是对图从结点S深度优先遍历一次的大致过程。

    在树中为了更形象的对这个算法进行描述,免不了多加了一些属性在其中。

    其中u是图中的一个节点:

    u.color:这个是用来描述当前结点u是什么颜色的,WHITE代表的是,u结点没有被访问。

    BLACK代表的是,以结点u为前驱的所有结点都已经被访问到了。

    而GRAY代表的是,位于二者{WHITE,BLACK}之间,即u被访问过了,但u的子结点并未被全部访问到。

    u.father: 这个其实在本篇文章中意义不大,是用于标记结点u的前驱结点在图G 的Adj[]数组中的位序的,

    同时也是用于标识G在跑完DFS算法的时候生成的深度优先树的。

    u.startTime:结点的这个属性是用来给每个结点盖上时间戳的,

    这个属性记载的是u结点第一次被发现的时间(涂上灰色的时候)

    u.endTime:同上,不过这个属性记载的是在搜索完成对u结点的邻接链表扫描结束之后的时间,

    该时间同样也是,访问从u出发的所有的结点之后,也是u结点被涂成黑色之后的时间。

    u.name: 这个是用来记录结点的名称的;

    u.firstedge:这个是邻接链表用于指向该结点后面的第一个邻接结点的指针,

    通过它可以找到图中与u结点相连接的其他结点。

    下面的深度优先搜索算法的伪代码将其发现结点u的时刻记录在属性u.d中,

    将其完成对结点u的处理的时刻记录在u.f这一属性中。

    因为|V|个结点中只能有一个发现事件和一个完成事件,

    所以这些时间戳都是位于[1, 2*|V|]之间的整数。

    很显然对于每个结点都有u.d < u.f

    结点u在时间u.d之前为WHITE,在介于[u.d, u.f]之间为GRAY

    在u.f之后为BLACK。

    下面的伪代码给出的是基本的深度优先搜索算法。输入图G既可以是有向图

    当然也可以是无向图,而变量为一个全局变量,用它来计算对每一个访问结点的时间戳。

     1 DFS(G)
     2     for each vertex u attibute G.V
     3            u.color = WHITE
     4            u.father = NIL
     5      time = 0
     6      
     7     for each vertec u attribute to G.V
     8             if u.color  ==  WHITE
     9                      DFS-VISIT(G,u)
    10 
    11 //code above is the main DFS that the cycle will check
    12 //each node in Graph G, if the node is unvisited whose color is white
    13 // DFS will call method:DFS-VISIT and take that node as the source node
    14 
    15 DFS-VISIT(G,u)
    16 time = time+1
    17 
    18 u.d = time
    19 
    20 u.color = GRAY
    21 
    22 for each v attribute to G:Adj[u]
    23   if v.color == WHITE
    24       v.f = u
    25       DFS-VISIT(G, v)  //continue to run
    26 
    27 u.color = BLACK
    28 //blacken the node u after its subnodes all visited
    29 
    30 time = time +1
    31 
    32 u.f = time

     下面是使用C++代码实现上述的伪代码:

     在这里说明一下,LZ将color属性单独提出来作为Color数组,这个Color数组

    其实质上与平时进行图遍历的visited数组所起到的作用是一样的,只不过,在《算法导论 第三版》

    中将结点划分为访问与未访问之间,又多出了一个状态:GRAY,就是一个结点虽然被访问,

    但是与它相连接的结点还没有全部被访问,这种情况下,将该结点的颜色属性置为GRAY。

     31 #include<stdio.h>
     32 #include<stdlib.h>
     33 
     34 #define MaxVertexNum 50
     35 
     36 
     37 typedef struct node
     38 {
     39     int adjvex;
     40     struct node* next;
     41 }EdgeNode;
     42 
     43 
     44 typedef struct vnode
     45 {
     46     char name;
     47     int father;
     48     int startTime;
     49     int endTime;
     50         
     51     EdgeNode *firstedge;
     52 
     53 }VertexNode, AdjList[MaxVertexNum];
     54 
     55 
     56 typedef struct
     57 {
     58     AdjList Adj;
     59     int edgeNum, vertexNum;
     60 }Graph;
     61 
     62 
     63 typedef enum {WHITE, GRAY, BLACK} color;
     64 
     65 
     66 color  Color[MaxVertexNum];
     67 
     68 int time;
     69 
     70 
     71 void InitGraph(Graph *G)
     72 {
     73     int i, j, k;
     74     
     75     EdgeNode *s;
     76         
     77     printf("Input VertexNum  and EdgeNum :  ");
     78     scanf("%d %d", &G->vertexNum, &G->edgeNum);
     79     getchar();
     80 
     81     for(i = 0 ; i < G->vertexNum; i++)
     82     {
     83             
     84 
     85             scanf("%c", &G->Adj[i].name);
     86             getchar();
     87             
    88 89 G->Adj[i].firstedge = NULL; 90 91 G->Adj[i].father = -1; 92 93 Color[i] = WHITE; 94 95 } 96 97 printf("input edges, Create Adjacency List : "); 98 99 for(k = 0 ; k < G->edgeNum; k++) 100 { 101 scanf("%d %d", &i, &j); 102 103 s = (EdgeNode*)malloc(sizeof(EdgeNode)); 104 105 s->adjvex = j; 106 107 s->next = G->Adj[i].firstedge; 108 109 G->Adj[i].firstedge = s; 110 111 112 113 /* if the graph is undirected graph, 114 release the following codes 115 116 s = (EdgeNode *) malloc(sizeof(EdgeNode)); 117 118 s->adjvex = i; 119 120 s->next = G->Adj[j].firstedge; 121 122 G->Adj[j].firstedge = s; 123 */ 124 } 125 } 126 127 128 129 130 131 void DFS_VISIT(Graph *G, int i) 132 { 133 EdgeNode *p; 134 135 time = time+1; 136 137 G->Adj[i].startTime = time; 138 139 Color[i] = GRAY; 140 141 p = G->Adj[i].firstedge; 142 143 while(p) 144 { 145 if(Color[p->adjvex]==WHITE) 146 { 147 G->Adj[p->adjvex].father = i; 148 DFS_VISIT(G, p->adjvex); 149 } 150 p = p->next; 151 } 152 153 Color[i] = BLACK; 154 time++; 155 156 G->Adj[i].endTime = time; 157 } 158 159 void DFS(Graph *G) 160 { 161 int i ; 162 163 time = 0; 164 for(i = 0; i < G->vertexNum; i++) 165 if(Color[i] == WHITE) 166 DFS_VISIT(G,i); 167 } 168 169 170 void PrintVisit(Graph *G, int source) 171 { 172 EdgeNode *p; 173 174 p = G->Adj[source].firstedge; 175 176 printf("current node name : %c ", G->Adj[source].name); 177 printf("current node connect to : "); 178 179 while(p) 180 { 181 printf("node:%c ", G->Adj[p->adjvex].name); 182 p = p->next; 183 } 184 185 printf(" "); 186 187 } 188 189 190 void PrintGraph(Graph *G) 191 { 192 int i; 193 for(i = 0 ; i < G->vertexNum ; i++) 194 PrintVisit(G , i ); 195 196 } 197 198 199 200 201 int main() 202 { 203 Graph G; 204 205 InitGraph(&G); 206 207 PrintGraph(&G); 208 209 DFS(&G); 210 211 return 0; 212 213 }

    根据上面所化的图进行数据的输入是这样的

    边:

    S Z Y W X T V U 

     点:(0,1) (0,3)  (1,2)  (1,3)  (2,4) (3,4)  (4,1)

    (6,0)  (6,3) (5,6) (5,7) (7,6) (7,5)

    对于点的输入是这样的,

    对应的是相应的点在G->Adj数组中的序号。

    在控制台输入的时候是不需要添加括号和中间逗号的,

    一组两个数,以空格分隔,回车换行接着输入下一条边。

    例如第一个输入的是S符号,则说明S在Adj中的位序为0

    而W是第四个输入的,在Adj中的位序3

    如果想表示S->W 这条有向边的话,则在边输入的时候,输入0(空格)3

    所以边的输入是要根据具体的点在Adj数组中的位序而定的。 

    深度搜索在本文中是以递归实现的,

    如果不适用递归的话,同样亦可以通过不断地压栈,

    然后不断地弹栈来实现针对某一个结点的不断深入地搜索。

    当 当前结点有三个子结点需要访问的时候,

    如果默认是左优先,则把靠右边的两个结点全部压栈。

    然后继续访问左结点的子结点,如此下去。

    一旦当某一个结点再也找不到子结点的时候,

    则弹栈,继续从弹出栈的结点进行深入访问,如此继续下去。

    直到所有的结点都被访问后结束。

  • 相关阅读:
    python之字典操作
    python之元组操作
    初始超算
    后缀自动机
    博弈
    曼哈顿最小生成树
    莫队算法
    主席树
    [HNOI2014]世界树
    [SDOI2011]消耗战
  • 原文地址:https://www.cnblogs.com/inuyasha1027/p/algorithm_DFS_Graph.html
Copyright © 2011-2022 走看看