forked from luoweipeter/C
-
Notifications
You must be signed in to change notification settings - Fork 0
/
GraphDFS.cpp
401 lines (331 loc) · 12.4 KB
/
GraphDFS.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define FALSE 0
#define TRUE 1
#define OK 1
#define ERROR 0
#define MAX_VERTEX_NUM 20
#define MAX_NAME 20
#define STACK_INIT_SIZE 10 // 存储空间初始分配量
#define STACKINCREMENT 2 // 存储空间分配增量
#define STACK_INIT_SIZE 10 //定义最初申请的内存的大小
#define STACK_INCREMENT 2 //每一次申请内存不足的时候扩展的大小
#define OVERFLOW false //异常抛出返回值
typedef int TElemType;
typedef int InfoType;
typedef int VertexType;
typedef bool Boolean;
typedef bool Status;
typedef int SElemType;
/******************************** 数据结构区 ********************************/
typedef struct CSNode //孩子兄弟树结点类型
{
TElemType data;
CSNode *firstchild, *nextsibling;
} CSNode, *CSTree; //CSTree是孩子兄弟树类型
struct ArcNode
{
int adjvex; // 该弧所指向的顶点的位置
ArcNode *nextarc; // 指向下一条弧的指针
InfoType *info; // 网的权值指针
}; // 表结点
typedef struct VNode
{
VertexType data; // 顶点信息
ArcNode *firstarc; // 第一个表结点的地址,指向第一条依附该顶点的弧的指针
} VNode, AdjList[MAX_VERTEX_NUM]; // 头结点
typedef struct
{
AdjList vertices; //以头结点表示邻接表
int vexnum, arcnum; // 图的当前顶点数和弧数
int kind; // 图的种类标志
} ALGraph;
struct Stack
{
//栈的数据元素类型在应用程序中定义typedef int SElemType;
SElemType *base; // 栈底指针
//在栈构造之前和销毁之后,base为NULL
SElemType *top; // 栈顶指针
//初值指向栈底,top = base可做栈空标记
//插入新栈顶元素时,top增1;删除栈顶元素时,top减1
//非空栈的top始终在栈顶元素的下一个位置上
int stacksize; // 当前已分配的存储空间,以元素为单位
//栈当前可使用的最大容量
}; // 顺序栈
/******************************** 基本函数 ********************************/
Boolean visited[MAX_VERTEX_NUM];
VertexType& GetVex(ALGraph G, int v)
{
// 初始条件: 无向图G存在,v是G中某个顶点的序号。操作结果: 返回v的值
if (v >= G.vexnum || v < 0)
exit(ERROR);
return G.vertices[v].data;
}
int LocateVex(ALGraph G, VertexType u)
{
// 初始条件: 图G存在,u和G中顶点有相同特征
// 操作结果: 若G中存在顶点u,则返回该顶点在图中位置;否则返回-1
int i;
for (i=0; i<G.vexnum; ++i) //依次扫描每个头结点的顶点信息
if (u== G.vertices[i].data) //如果当前头结点的顶点信息等于u
return i; //返回当前头结点的序号
return -1;
}
Status CreateGraph(ALGraph &G)
{
// 采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图)
int i, j, k;
int w; // 权值
VertexType va, vb; //顶点va、vb
ArcNode *p; //表结点指针p
printf("请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3):(这里以无向网为例)\n ");
scanf("%d", &G.kind);
printf("请输入图的顶点数 边数: ");
scanf("%d%d", &G.vexnum, &G.arcnum);
//MAX_NAME,顶点字符串的最大长度+1
printf("请输入%d个顶点的值(<%d个字符):\n", G.vexnum, MAX_NAME);
for (i=0; i<G.vexnum; ++i) // 构造顶点向量,构造各个头结点
{
scanf("%d",&G.vertices[i].data); //为各个头结点输入名字
G.vertices[i].firstarc = NULL; //链域置空
}
if (G.kind == 1 || G.kind == 3) // 网
printf("请顺序输入每条弧(边)的权值、弧尾和弧头(以空格作为间隔):\n");
else // 图
printf("请顺序输入每条弧(边)的弧尾和弧头(以空格作为间隔):\n");
for (k=0; k<G.arcnum; ++k) // 构造表结点链表
{
if (G.kind == 1 || G.kind == 3) // 网
scanf("%d%d%d", &w,&va,&vb); //接收每条弧(边)的权值、弧尾和弧头
else // 图
scanf("%d%d",&va,&vb); //接收每条弧(边)的弧尾和弧头
i = LocateVex(G, va); // i取弧尾序号
j = LocateVex(G, vb); // j取弧头序号
//p指向新分配的表结点空间
p = (ArcNode*) malloc(sizeof (ArcNode));
p->adjvex = j; //该弧所指向的顶点的位置,为什么是弧头?为求入度,假如是弧尾,则是求出度
if (G.kind == 1 || G.kind == 3) // 网
{
p->info = (int *) malloc(sizeof (int)); //分配网的权值的空间
*(p->info) = w; //网的权值取w
}
else
p->info = NULL; // 图
//新弧p的下一条弧取当前头结点的第一条弧
p->nextarc = G.vertices[i].firstarc; // 插在表头
G.vertices[i].firstarc = p; //当前头结点的第一条弧取新弧p
if (G.kind >= 2) // 无向图或网,产生第二个表结点
{
p = (ArcNode*) malloc(sizeof (ArcNode)); //分配表结点
p->adjvex = i; //该弧所指向的顶点的位置,取弧尾
if (G.kind == 3) // 无向网
{
p->info = (int*) malloc(sizeof (int)); //分配网的权值的空间
*(p->info) = w; //网的权值取w
}
else
p->info = NULL; // 无向图
//新弧p的下一条弧取当前头结点的第一条弧
p->nextarc = G.vertices[j].firstarc; // 插在表头
G.vertices[j].firstarc = p; //当前头结点的第一条弧取新弧p
}
}
return OK;
}
int FirstAdjVex(ALGraph G, VertexType v)
{
// 初始条件: 图G存在,v是G中某个顶点
// 操作结果: 返回v的第一个邻接顶点的序号。若顶点在G中没有邻接顶点,则返回-1
ArcNode *p;
int v1;
v1 = LocateVex(G, v); // v1取顶点v的序号
p = G.vertices[v1].firstarc; //p指向顶点v的第一条弧
if (p)
return p->adjvex; //返回顶点v的第一条弧所指向顶点的序号
else
return -1;
}
int NextAdjVex(ALGraph G, VertexType v, VertexType w)
{
// 初始条件: 图G存在,v是G中某个顶点,w是v的邻接顶点
// 操作结果: 返回v的(相对于w的)下一个邻接顶点的序号。
// 若w是v的最后一个邻接点,则返回-1
ArcNode *p;
int v1, w1;
v1 = LocateVex(G, v); // v1取顶点v的序号
w1 = LocateVex(G, w); // w1取顶点w的序号
p = G.vertices[v1].firstarc; //p指向顶点v的第一条弧
while (p && p->adjvex != w1) // 顶点v的第一条弧存在、且顶点v的第一条弧所指顶点不是w
p = p->nextarc; //p后移,依次指向顶点v的第二、第三、第...条弧
//循环结束时,顶点v的第...条弧不存在、或者顶点v的第...条弧所指顶点是w
if (!p || !p->nextarc) // 没找到w、或w是最后一个邻接点
//p->nextarc为NULL说明w是最后一个邻接点
return -1;
else // p->adjvex == w
return p->nextarc->adjvex; // 返回v的(相对于w的)下一个邻接顶点的序号
}
void DFSTree(ALGraph G, int v, CSTree &T)
{
// 从第v个顶点出发深度优先遍历图G,建立以T为根的生成树。算法7.8
Boolean first = TRUE;
int w;
CSTree p, q; //孩子兄弟树p、q
VertexType v1, w1;
visited[v] = TRUE;
v1=GetVex(G, v); //顶点v1序号为v
// w依次为v的邻接顶点
//w初始化为v1第一个邻接点序号
//只要w大于等于0,就执行循环体
//循环体完毕,w取v1的(相对于w1(序号w)的)下一个邻接顶点的序号,准备下轮循环
for (w=FirstAdjVex(G,v1); w>=0; w=NextAdjVex(G,v1,w1=GetVex(G,w)))
if (!visited[w]) // w顶点不曾被访问
{
p = (CSTree) malloc(sizeof (CSNode)); // 分配新孩子结点
p->data=GetVex(G, w); //v的当前邻接顶点复制到新结点
p->firstchild = NULL;
p->nextsibling = NULL;
if (first) // w是v的第一个未被访问的邻接顶点
{
T->firstchild = p; //树T的长子取新结点
first = FALSE; // 长子标志置假
}
else // w是v的其它未被访问的邻接顶点,是上一邻接顶点的兄弟结点
q->nextsibling = p; // 上一邻接顶点的兄弟结点取新结点
q = p; //q取新结点
DFSTree(G, w, q); // 从第w个顶点出发深度优先遍历图G,递归建立以q为根的子生成树
}
}
void DFSForest(ALGraph G, CSTree &T)
{
// 建立无向图G的深度优先生成森林的(最左)孩子(右)兄弟链表T。算法7.7
CSTree p, q; //孩子兄弟树p、q
int v;
T = NULL;
for (v=0; v<G.vexnum; ++v)
visited[v] = FALSE; // 赋初值
for (v=0; v<G.vexnum; ++v) // 从第0个顶点找起
if (!visited[v]) // 第v个顶点不曾被访问
{
// 第v顶点为新的生成树的根结点
p = (CSTree) malloc(sizeof (CSNode)); // 分配根结点
p->data=GetVex(G, v); //当前顶点(第v个)拷贝到新分配的根结点
p->firstchild = NULL;
p->nextsibling = NULL;
if (!T) // 如果孩子兄弟树T为空,表示是第一棵生成树的根(T的根)
T = p; //所以T取新分配的根结点
else // 如果孩子兄弟树T不为空,表示是其它生成树的根(前一棵的根的“兄弟”)
q->nextsibling = p; //令前一棵的根的“兄弟”取新分配的根结点
q = p; // q取当前生成树的根
DFSTree(G, v, p); // 从第v顶点出发建立以p为根的生成树
}
}
Status OutPutALGraph(ALGraph G)
{
//以无向网为例
int i;
for(i=0; i<G.vexnum; i++)
{
printf("%s\t",G.vertices[i].data);
while(G.vertices[i].firstarc)
{
printf("%d\t",G.vertices[i].firstarc->adjvex);
G.vertices[i].firstarc=G.vertices[i].firstarc->nextarc;
}
printf("\n");
}
return 0;
}
Status InitTree(CSTree &T)
{
// 操作结果: 构造空树T
T = NULL;
return OK;
}
/********************** 构造一个空栈S **********************/
Status InitStack(Stack &S)
{
// 构造一个空栈S
//栈底指针S.base指向新分配的STACK_INIT_SIZE个SElemType大小的空间
if (!(S.base = (SElemType *) malloc(STACK_INIT_SIZE * sizeof (SElemType))))
exit(OVERFLOW); // 存储分配失败
//必须给SqStack结构对象S的3个成员赋值
S.top = S.base; //空栈的栈顶指针S.top = 栈底指针S.base
S.stacksize = STACK_INIT_SIZE; //初始栈容量
return OK;
}
/********************** 插入新的栈顶元素 **********************/
Status Push(Stack &S, SElemType e)
{
// 插入元素e为新的栈顶元素
// if (S.top - S.base >= S.stacksize) // 栈满,追加存储空间
//如果栈长度大于栈容量
// {
//可能会改变栈底地址,新栈底指针S.base
// S.base = (SElemType *) realloc(S.base, //原栈底指针
// (S.stacksize + STACKINCREMENT) * sizeof (SElemType)); //新大小
// if (!S.base)
// exit(OVERFLOW); // 存储分配失败
//
//给SqStack结构对象S的3个成员赋值
// S.top = S.base + S.stacksize;
// S.stacksize += STACKINCREMENT;
// }
*(S.top)++ = e; //先把e压入栈顶,S.top再增1指向栈顶元素e的下一个位置
return OK;
}
/********************** 删除栈顶元素 **********************/
Status Pop(Stack &S)
{
// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
int e;
if (S.top == S.base) //如果空栈,报错
return ERROR;
e = *--S.top; //S.top先减1指向栈顶元素,再取值,交给e带回
return OK;
}
/********************** 判断栈是否是空的 **********************/
Status output(int e)
{
printf("%d",e);
return 0;
}
Status StackTraverse(Stack S, Status (*visit) (SElemType))
{
// 从栈底到栈顶依次对栈中每个元素调用函数visit()。
// 一旦visit()失败,则操作失败
while (S.top > S.base) //从栈底元素开始,到栈顶元素为止
visit(*S.base++); //依次取栈底指针指向元素调用visit(),栈底指针后(上)移
//循环结束时,S.base = S.top
printf("\n");
return OK;
}
void OutPath(CSTree T)
{
// 依次从左到右输出森林中每一棵树中从根到叶的路径
// 森林的存储结构为孩子-兄弟链表,T为头指针
Stack S; //栈S存储路径
InitStack(S);
if (T)
{
Push(S,T->data); // 根结点加入路径S
if (!T->firstchild)
StackTraverse(S, output); // 求得一条路径
//从栈底到栈顶依次对栈S中每个元素调用函数output()
else
OutPath(T->firstchild); // 递归,遍历子树森林
Pop(S); // 从路径中删除栈顶结点
OutPath(T->nextsibling); // 递归,遍历其余树的森林
}
}
/******************************** 主函数 ********************************/
int main()
{
ALGraph G;
CSTree T;
CreateGraph(G);
InitTree(T);
DFSForest(G,T);
OutPath(T);
return 0;
}