1. 1 #include<stdio.h>
  2. 2 #include<stdlib.h>
  3. 3 #include<string.h>
  4. 4 #define TRUE 1
  5. 5 #define FALSE 0
  6. 6 #define inf 99999
  7. 7 typedef int BOOL;
  8. 8 typedef int ElemType;
  9. 9 typedef struct mGraph
  10. 10 {
  11. 11 ElemType **a; //邻接矩阵
  12. 12 int n; //图的当前顶点数
  13. 13 int e; //图的当前边数
  14. 14 ElemType noEdge; //两顶点间无边时的值
  15. 15 }MGraph;
  16. 16 typedef struct queue
  17. 17 {
  18. 18 int front; //队头
  19. 19 int rear; //队尾
  20. 20 int MaxSize; //队的最大容量
  21. 21 ElemType *element; //存储元素
  22. 22 }Queue;
  23. 23 typedef struct eNode
  24. 24 {
  25. 25 int adjVex; //与任意顶点u相邻接的顶点
  26. 26 ElemType w; //边的权值
  27. 27 struct eNode *nextArc; //指向下一个结点
  28. 28 }ENode;
  29. 29 typedef struct lGraph
  30. 30 {
  31. 31 int n; //图当前的顶点数
  32. 32 int e; //图当前的边数
  33. 33 ENode **a; //指向一维指针数组
  34. 34 }LGraph;
  35. 35 typedef struct stack
  36. 36 {
  37. 37 int top; //栈顶
  38. 38 int maxSize; //栈所能装最大元素量
  39. 39 ElemType *element; //数据
  40. 40 }Stack;
  41. 41
  42. 42 int vis_m[1000]; //邻接矩阵对应图的标记数组。 vis_m[i]用于表示 编号为i的结点是否被使用过,没有使用过则 =0,否则 =1;
  43. 43 int vis_l[1000]; //邻接表对应图的标记数组。 vis_m[i]用于表示 编号为i的结点是否被使用过,没有使用过则 =0,否则 =1;
  44. 44 int path[1000]; //用于记录最佳路径上的结点编号
  45. 45 int Min_Time=999999; //用于记录最佳路径所花费的时间,初始化时尽量使它大
  46. 46 int count; //记录最佳路径上结点的个数
  47. 47 int load[1000]; //用于图的遍历,让遍历路径数组的储存空间足够大
  48. 48 int cnt; //用于表示load[]数组的下标
  49. 49
  50. 50 void Clear_Flag() //标记清零操作
  51. 51 {
  52. 52 int i;
  53. 53 cnt=0;
  54. 54 for( i=0;i<1000;i++ )
  55. 55 load[i]=vis_m[i]=vis_l[i]=0;
  56. 56 }
  57. 57
  58. 58 void Create_S( Stack *s,int size ) //创建栈
  59. 59 {
  60. 60 s->maxSize=size;
  61. 61 s->top=-1; //现在栈还是空的,所以栈顶为“-1”
  62. 62 s->element=(ElemType*)malloc(sizeof(ElemType));
  63. 63 }
  64. 64 void Destroy_S( Stack *s ) //销毁栈
  65. 65 {
  66. 66 s->top=-1;
  67. 67 s->maxSize=0;
  68. 68 free(s->element); //因为只申请了一次,所以只释放一次
  69. 69 }
  70. 70 BOOL IsEmpty_S( Stack *s ) //判断栈是否为空栈
  71. 71 {
  72. 72 return s->top==-1;
  73. 73 }
  74. 74 BOOL IsFull_S( Stack *s ) //判断一个栈是否已满
  75. 75 {
  76. 76 return s->top==s->maxSize-1;
  77. 77 }
  78. 78 void Top_S( Stack *s,ElemType *x ) //获取栈顶元素
  79. 79 {
  80. 80 if( IsEmpty_S(s) ) //如果栈为空
  81. 81 {
  82. 82 printf("现在栈为空栈,没有东西可取喔。\n");
  83. 83 return ;
  84. 84 }
  85. 85 *x=s->element[s->top];
  86. 86 }
  87. 87 void Push_S( Stack *s,ElemType x ) //入栈
  88. 88 {
  89. 89 if( IsFull_S(s) ) //如果栈已满
  90. 90 {
  91. 91 printf("现在栈已满,不能再装东西啦喔。\n");
  92. 92 return ;
  93. 93 }
  94. 94 s->top++;
  95. 95 s->element[s->top]=x;
  96. 96 }
  97. 97 void Pop_S( Stack *s ) //删除栈顶元素
  98. 98 {
  99. 99 if( IsEmpty_S(s) ) //如果栈为空
  100. 100 {
  101. 101 printf("现在栈为空栈喔。\n");
  102. 102 return ;
  103. 103 }
  104. 104 s->top--;
  105. 105 }
  106. 106 void Clear_S( Stack *s ) //清空栈,但不释放空间
  107. 107 {
  108. 108 s->top=-1;
  109. 109 }
  110. 110
  111. 111 void Create_Q( Queue *q,int size ) //创建队列
  112. 112 {
  113. 113 q->front=q->rear=0; //创建时,这里为0,都指向队头
  114. 114 q->MaxSize=size;
  115. 115 q->element=(ElemType*)malloc(size*sizeof(ElemType));
  116. 116 }
  117. 117 void Destroy_Q( Queue *q )
  118. 118 {
  119. 119 q->front=q->rear=-1;
  120. 120 q->MaxSize=0;
  121. 121 free(q->element);
  122. 122 }
  123. 123 void Clear_Q( Queue *q )
  124. 124 {
  125. 125 q->front=q->rear=0; //注意这里也是0,清空时,让它俩都指向队头
  126. 126 }
  127. 127 BOOL IsEmpty_Q( Queue *q ) //判断队列是否为空
  128. 128 {
  129. 129 return q->front==q->rear;
  130. 130 }
  131. 131 BOOL IsFull_Q( Queue *q ) //判断队列是否已满
  132. 132 {
  133. 133 return (q->rear+1)%q->MaxSize==q->front;
  134. 134 }
  135. 135 void Push_Q( Queue *q,ElemType x )
  136. 136 {
  137. 137 if( IsFull_Q(q) )
  138. 138 {
  139. 139 printf("队列已满,无法再进行入队操作!\n");
  140. 140 return ;
  141. 141 }
  142. 142 q->rear=(q->rear+1)%q->MaxSize;
  143. 143 q->element[q->rear]=x;
  144. 144 }
  145. 145 void DeQueue_Q( Queue *q ) //删除队头元素,出队操作
  146. 146 {
  147. 147 if( IsEmpty_Q(q) )
  148. 148 {
  149. 149 printf("队列为空,无法再进行出队操作!\n");
  150. 150 return ;
  151. 151 }
  152. 152 q->front=(q->front+1)%q->MaxSize;
  153. 153 }
  154. 154 void Front_Q( Queue *q,ElemType *x ) //获取队头元素
  155. 155 {
  156. 156 if( IsEmpty_Q(q) )
  157. 157 {
  158. 158 printf("队列为空,无法获取队头元素!\n");
  159. 159 return ;
  160. 160 }
  161. 161 *x=q->element[(q->front+1)%q->MaxSize];
  162. 162 }
  163. 163
  164. 164
  165. 165 void Init_M( MGraph *mg,int v,ElemType noEdge ) //v为顶点数,noEdge为:两顶点无边时的值
  166. 166 {
  167. 167 int i,j;
  168. 168 mg->n=v;
  169. 169 mg->e=0; //初始化时没有边
  170. 170 mg->noEdge=noEdge;
  171. 171 mg->a=(ElemType**)malloc(mg->n*sizeof(ElemType*)); //申请二维动态空间
  172. 172 for( i=0;i<mg->n;i++ )
  173. 173 {
  174. 174 mg->a[i]=(ElemType*)malloc(mg->n*sizeof(ElemType)); //申请一维动态空间
  175. 175 for( j=0;j<mg->n;j++ )
  176. 176 mg->a[i][j]=mg->noEdge;
  177. 177 mg->a[i][i]=0;
  178. 178 }
  179. 179 printf("邻接矩阵已成功初始化!\n");
  180. 180 return ;
  181. 181 }
  182. 182 void Destroy_M( MGraph *mg )
  183. 183 {
  184. 184 int i;
  185. 185 for( i=0;i<mg->n;i++ ) //申请了几次一维动态空间就要释放几次
  186. 186 free(mg->a[i]);
  187. 187 free(mg->a);
  188. 188 printf("邻接矩阵已经成功撤销!\n");
  189. 189 return ;
  190. 190 }
  191. 191 BOOL Find_M_E( MGraph *mg,int u,int v ) //邻接矩阵的边的搜索,u为出发点,v为被指向点
  192. 192 {
  193. 193 if( u<0||u>=mg->n||v<0||v>=mg->n||u==v ) //增强代码的健壮性,判断错误输出
  194. 194 {
  195. 195 printf("请输入正确的 你所要找查的边的两端的结点值!\n");
  196. 196 return FALSE;
  197. 197 }
  198. 198 if( mg->a[u][v]==mg->noEdge ) //如果 u→v 没有边
  199. 199 return FALSE;
  200. 200 else
  201. 201 return TRUE;
  202. 202 }
  203. 203 void Insert_M_E( MGraph *mg,int u,int v,ElemType x ) //邻接矩阵的边的插入
  204. 204 {
  205. 205 if( u<0||u>=mg->n||v<0||v>=mg->n||u==v ) //增强代码的健壮性,判断错误输出
  206. 206 {
  207. 207 printf("请输入正确的 你所要找插入的边的两端的结点值!\n");
  208. 208 return ;
  209. 209 }
  210. 210 if( Find_M_E(mg,u,v) ) //如果该边的已经有了,则不能在进行重复插入操作
  211. 211 {
  212. 212 printf("该边已经在图中存在了,请勿重复插入!\n");
  213. 213 }
  214. 214 else
  215. 215 {
  216. 216 mg->a[u][v]=x; //执行插入操作
  217. 217 mg->e++;
  218. 218 }
  219. 219 }
  220. 220 BOOL Delete_M_E( MGraph *mg,int u,int v ) //邻接矩阵的边的删除
  221. 221 {
  222. 222 if( u<0||u>=mg->n||v<0||v>=mg->n||u==v ) //增强代码的健壮性,判断错误输出
  223. 223 {
  224. 224 printf("请输入正确的 你所要找删除的边的两端的结点值!\n");
  225. 225 return FALSE;
  226. 226 }
  227. 227 if( !Find_M_E(mg,u,v) ) //如果该边的在图中本身就没有,则不能在进行删除操作
  228. 228 {
  229. 229 printf("该边在图中不存在!\n");
  230. 230 return FALSE;
  231. 231 }
  232. 232 else
  233. 233 {
  234. 234 mg->a[u][v]=mg->noEdge;
  235. 235 mg->e--;
  236. 236 return TRUE;
  237. 237 }
  238. 238 }
  239. 239
  240. 240 //以上述邻接矩阵为存储结构,编写程序,实现图的深度优先遍历、宽度优先遍历
  241. 241 void Dfs_M( MGraph *mg,int u ) //u为开始遍历的起点
  242. 242 {
  243. 243 int i=0;
  244. 244 if( !vis_m[u] )
  245. 245 {
  246. 246 printf("%3d ",u);
  247. 247 load[cnt++]=u;
  248. 248 vis_m[u]=1; //编号为u的结点 的标记改变
  249. 249 }
  250. 250 while( i<mg->n )
  251. 251 {
  252. 252 if( !vis_m[i]&&mg->a[u][i]!=mg->noEdge&&u!=i ) //如果u→编号为i的结点有边,并且结点i未被“走过”(即标记过)
  253. 253 {
  254. 254 Dfs_M( mg,i );
  255. 255 }
  256. 256 i++;
  257. 257 }
  258. 258 }
  259. 259 void Bfs_M( MGraph *mg,int u ) //u为开始遍历的起点
  260. 260 {
  261. 261 int i;
  262. 262 Queue q;
  263. 263 Create_Q(&q,mg->n); //创建队列,最大容量要+1才行
  264. 264 vis_m[u]=1; //编号为u的结点 的标记改变
  265. 265 Push_Q(&q,u); //编号为u的结点 压入
  266. 266 while( !IsEmpty_Q(&q) ) //只要队列还不为空,就继续执行
  267. 267 {
  268. 268 Front_Q(&q,&u); //将队头元素传给u
  269. 269 printf("%3d ",u); //打印
  270. 270 load[cnt++]=u;
  271. 271 DeQueue_Q(&q); //将队头元素去掉
  272. 272 for( i=0;i<mg->n;i++ )
  273. 273 {
  274. 274 if( !vis_m[i]&&mg->a[u][i]!=mg->noEdge&&u!=i ) //如果u→编号为i的结点有边,并且结点i未被“走过”(即标记过)
  275. 275 {
  276. 276 Push_Q(&q,i);
  277. 277 vis_m[i]=1; //标记改变
  278. 278 }
  279. 279 }
  280. 280 }
  281. 281 Destroy_Q(&q); //撤销队列
  282. 282 return ;
  283. 283 }
  284. 284
  285. 285 void Init_L( LGraph *lg,int size ) //x表示顶点数的意思
  286. 286 {
  287. 287 int i;
  288. 288 lg->n=size;
  289. 289 lg->e=0; //刚开始图是没有边的
  290. 290 lg->a=(ENode**)malloc(lg->n*sizeof(ENode*));
  291. 291 for( i=0;i<lg->n;i++ )
  292. 292 lg->a[i]=NULL; //将指针数组a置空
  293. 293 return ;
  294. 294 }
  295. 295 void Destroy_L( LGraph *lg )
  296. 296 {
  297. 297 ENode *front,*cur; //指针cur用于释放,指针front用于遍历
  298. 298 int i;
  299. 299 for( i=0;i<lg->n;i++ )
  300. 300 {
  301. 301 front=lg->a[i]; //指针front指向顶点i的单链表的第一个边结点
  302. 302 cur=front;
  303. 303 while( cur )
  304. 304 {
  305. 305 front=front->nextArc;
  306. 306 free(cur);
  307. 307 cur=front;
  308. 308 }
  309. 309 }
  310. 310 free( lg->a ); //释放一维指针数组a的存储空间
  311. 311 return ;
  312. 312 }
  313. 313 BOOL Find_L_E( LGraph *lg,int u,int v ) //邻接表 边的搜索
  314. 314 {
  315. 315 ENode *fp=lg->a[u];
  316. 316 if( u<0||u>=lg->n||v<0||v>=lg->n||u==v ) //增强代码的健壮性,判断错误输出
  317. 317 {
  318. 318 printf("请输入正确的 你所要找查的边的两端的结点值!\n");
  319. 319 return FALSE;
  320. 320 }
  321. 321 while( fp&&fp->adjVex!=v )
  322. 322 {
  323. 323 fp=fp->nextArc;
  324. 324 }
  325. 325 if( fp!=NULL ) //若找到了这条边
  326. 326 return TRUE;
  327. 327 else
  328. 328 return FALSE;
  329. 329 }
  330. 330 void Insert_L_E( LGraph *lg,int u,int v,ElemType w )
  331. 331 {
  332. 332 ENode *fp; //front作为前驱指针,指向插入位置的前一个结点
  333. 333 if( u<0||u>=lg->n||v<0||v>=lg->n||u==v ) //增强代码的健壮性,判断错误输出
  334. 334 {
  335. 335 printf("请输入正确的 你所要找插入的边的两端的结点值!\n");
  336. 336 return ;
  337. 337 }
  338. 338 if( Find_L_E(lg,u,v) ) //如果在图中找到这条边
  339. 339 {
  340. 340 printf("该边已经在图中存在了,请勿重复插入!\n");
  341. 341 return ;
  342. 342 }
  343. 343 else
  344. 344 {
  345. 345 fp=(ENode*)malloc(sizeof(ENode)); //为新的边结点分配存储空间
  346. 346 fp->adjVex=v;
  347. 347 fp->w=w;
  348. 348 fp->nextArc=lg->a[u]; //将新的边结点插入单链表的最前面
  349. 349 lg->a[u]=fp;
  350. 350 lg->e++; //细节
  351. 351 }
  352. 352 }
  353. 353 BOOL Delete_L_E( LGraph *lg,int u,int v )
  354. 354 {
  355. 355 ENode *front,*fp; //一般情况下,前驱结点front指向删除结点fp 的前一个结点
  356. 356 if( u<0||u>=lg->n||v<0||v>=lg->n||u==v ) //增强代码的健壮性,判断错误输出
  357. 357 {
  358. 358 printf("请输入正确的 你所要删除的边的两端的结点值!\n");
  359. 359 return FALSE;
  360. 360 }
  361. 361 front=NULL;
  362. 362 fp=lg->a[u];
  363. 363 while( fp&&fp->adjVex!=v ) //找查待删除的边是否存在
  364. 364 {
  365. 365 front=fp;
  366. 366 fp=fp->nextArc;
  367. 367 }
  368. 368 if( fp==NULL ) //指针fp为空,说明该边不存在
  369. 369 {
  370. 370 printf("你所要删除的边在图中不存在!\n");
  371. 371 return FALSE;
  372. 372 }
  373. 373 if( front==NULL ) //前驱结点为空,说明,要删除的边在表头
  374. 374 {
  375. 375 lg->a[u]=fp->nextArc;
  376. 376 free(fp);
  377. 377 }
  378. 378 else //否则要删除的边不在表头
  379. 379 {
  380. 380 front->nextArc=fp->nextArc;
  381. 381 free(fp);
  382. 382 }
  383. 383 lg->e--; //细节
  384. 384 return TRUE;
  385. 385 }
  386. 386
  387. 387 /* 以上述邻接表为存储结构,编写程序,实现图的深度、宽度优先遍历 */
  388. 388 void Dfs_L( LGraph *lg,int u ) //u为开始遍历的起点
  389. 389 {
  390. 390 ENode *fp;
  391. 391 printf("%3d ",u);
  392. 392 load[cnt++]=u;
  393. 393 vis_l[u]=1; //标记改变
  394. 394 for( fp=lg->a[u];fp;fp=fp->nextArc )
  395. 395 {
  396. 396 if( !vis_l[fp->adjVex] ) //只要下一结点未被访问过
  397. 397 {
  398. 398 Dfs_L(lg,fp->adjVex);
  399. 399 }
  400. 400 }
  401. 401 return ;
  402. 402 }
  403. 403 void Bfs_L( LGraph *lg,int u )
  404. 404 {
  405. 405 ENode *fp; //移动指针,在邻接表上移动
  406. 406 Queue q;
  407. 407 Create_Q(&q,lg->n);
  408. 408 Push_Q(&q,u); //将编号为u的结点入队
  409. 409 while( !IsEmpty_Q(&q) ) //只要队列还不为空,就继续执行
  410. 410 {
  411. 411 Front_Q(&q,&u);
  412. 412 DeQueue_Q(&q);
  413. 413 printf("%3d ",u); //打印
  414. 414 load[cnt++]=u;
  415. 415 vis_l[u]=1; //标记改变
  416. 416 for( fp=lg->a[u];fp;fp=fp->nextArc )
  417. 417 {
  418. 418 if( !vis_l[fp->adjVex] ) //只要与u连接的结点未被访问过,就让他们入队
  419. 419 {
  420. 420 Push_Q(&q,fp->adjVex);
  421. 421 vis_l[fp->adjVex]=1; //标记改变
  422. 422 }
  423. 423 }
  424. 424 }
  425. 425 Destroy_Q(&q); //撤销队列
  426. 426 return ;
  427. 427 }
  428. 428
  429. 429 void Print_M( MGraph *mg ) //邻接矩阵打印
  430. 430 {
  431. 431 int i,j;
  432. 432 printf("\n\n该图的邻接矩阵为:\n");
  433. 433 for( i=0;i<mg->n;i++ )
  434. 434 {
  435. 435 for( j=0;j<mg->n;j++ )
  436. 436 {
  437. 437 if( mg->a[i][j]!=mg->noEdge )
  438. 438 printf("%d\t",mg->a[i][j]);
  439. 439 else
  440. 440 printf("*\t");
  441. 441 }
  442. 442 printf("\n");
  443. 443 }
  444. 444 printf("(注:“*”代表结点u到结点v没有边)\n");
  445. 445 }
  446. 446 void Print_L( LGraph *lg ) //邻接表打印
  447. 447 {
  448. 448 ENode *fp; //移动指针,在邻接表上移动
  449. 449 int i;
  450. 450 printf("\n\n该图的邻接表为:(注:“ |2| 1-50 ”代表的意思是,表头结点2,有指向结点1的边,边的值为50)\n");
  451. 451 for( i=0;i<lg->n;i++ )
  452. 452 {
  453. 453 printf("表头结点: |%d|",i);
  454. 454 for( fp=lg->a[i];fp;fp=fp->nextArc )
  455. 455 {
  456. 456 printf("%3d-%-3d ",fp->adjVex,fp->w);
  457. 457 }
  458. 458 printf("\n");
  459. 459 }
  460. 460 }
  461. 461 void Print_Indegree_1( MGraph *mg ) //(邻接矩阵)每个结点的入度打印,load[]为遍历路径
  462. 462 {
  463. 463 int i,j,ans;
  464. 464 int *fp;
  465. 465 for( i=0;i<cnt;i++ )
  466. 466 {
  467. 467 ans=0;
  468. 468 for( j=0;j<cnt;j++ )
  469. 469 {
  470. 470 fp=&(mg->a[j][ load[i] ]); //以第一排的每个元素 作起始地址 进行向下搜索
  471. 471 if( *fp!=0&&*fp!=mg->noEdge )
  472. 472 ans++;
  473. 473 }
  474. 474 printf("%3d ",ans);
  475. 475 }
  476. 476 }
  477. 477 void Print_Outdegree_1( MGraph *mg ) //(邻接矩阵)每个结点的出度打印,load[]为遍历路径
  478. 478 {
  479. 479 int i,j,ans; //ans表示结点度数
  480. 480 int *fp;
  481. 481 for( i=0;i<cnt;i++ )
  482. 482 {
  483. 483 ans=0;
  484. 484 fp=mg->a[ load[i] ];
  485. 485 for( j=0;j<cnt;j++ )
  486. 486 {
  487. 487 if( *fp!=0&&*fp!=mg->noEdge )
  488. 488 ans++;
  489. 489 fp++; //指针向右移动
  490. 490 }
  491. 491 printf("%3d ",ans);
  492. 492 }
  493. 493 }
  494. 494 void Print_Indegree_2( LGraph *lg ) //(邻接表)每个结点的入度打印,load[]为遍历路径
  495. 495 {
  496. 496 int i;
  497. 497 ENode *fp;
  498. 498 int *in_degree=(int*)malloc(lg->n*sizeof(int)); //用数组in_degree[]记录每个结点的入度
  499. 499 for( i=0;i<lg->n;i++ ) //初始化
  500. 500 in_degree[i]=0;
  501. 501 for( i=0;i<lg->n;i++ )
  502. 502 {
  503. 503 for( fp=lg->a[ load[i] ];fp;fp=fp->nextArc )
  504. 504 {
  505. 505 in_degree[fp->adjVex]++; //编号为 fp->adjVex 的结点的编号 的入度加1
  506. 506 }
  507. 507 }
  508. 508 for( i=0;i<lg->n;i++ )
  509. 509 {
  510. 510 printf("%3d ",in_degree[ load[i] ] );
  511. 511 }
  512. 512 }
  513. 513 void Print_Outdegree_2( LGraph *lg ) //(邻接表)每个结点的出度打印,load[]为遍历路径
  514. 514 {
  515. 515 int i,ans; //ans表示结点度数
  516. 516 ENode *fp;
  517. 517 for( i=0;i<lg->n;i++ )
  518. 518 {
  519. 519 ans=0;
  520. 520 for( fp=lg->a[ load[i] ];fp;fp=fp->nextArc )
  521. 521 {
  522. 522 ans++;
  523. 523 }
  524. 524 printf("%3d ",ans);
  525. 525 }
  526. 526 }
  527. 527
  528. 528 void Bfs_Judge( LGraph *lg,int u ) //用于判断是否是强连通图的辅助函数
  529. 529 {
  530. 530 ENode *fp; //移动指针,在邻接表上移动
  531. 531 Queue q;
  532. 532 Create_Q(&q,lg->n);
  533. 533 Push_Q(&q,u); //将编号为u的结点入队
  534. 534 while( !IsEmpty_Q(&q) ) //只要队列还不为空,就继续执行
  535. 535 {
  536. 536 Front_Q(&q,&u);
  537. 537 DeQueue_Q(&q);
  538. 538 cnt++;
  539. 539 vis_l[u]=1; //标记改变
  540. 540 for( fp=lg->a[u];fp;fp=fp->nextArc )
  541. 541 {
  542. 542 if( !vis_l[fp->adjVex] ) //只要与u连接的结点未被访问过,就让他们入队
  543. 543 {
  544. 544 Push_Q(&q,fp->adjVex);
  545. 545 vis_l[fp->adjVex]=1; //标记改变
  546. 546 }
  547. 547 }
  548. 548 }
  549. 549 Destroy_Q(&q); //撤销队列
  550. 550 return ;
  551. 551 }
  552. 552 BOOL Judge_QiangLiTongTu( LGraph *lg ) //判断是否是强连通图(针对于有向图)
  553. 553 {
  554. 554 int i;
  555. 555 for( i=0;i<lg->n;i++ )
  556. 556 {
  557. 557 Clear_Flag(); //标记清零操作
  558. 558 Bfs_Judge(lg,i);
  559. 559 if( cnt!=lg->n ) //若任意一对顶点之间都存在路径,则以某一结点为起点宽度遍历后,路径长度cnt应该为lg->n (注:cnt为全局变量)
  560. 560 return FALSE; //不是连通图,返回FALSE
  561. 561 }
  562. 562 return TRUE; //每个结点都满足,这说明是强连通图
  563. 563 }
  564. 564
  565. 565 void In_Degree_Helper( int *in_degree,LGraph *lg )
  566. 566 {
  567. 567 ENode *fp;
  568. 568 int i;
  569. 569 for( i=0;i<lg->n;i++ )
  570. 570 in_degree[i]=0; //先初始化
  571. 571 for( i=0;i<lg->n;i++ )
  572. 572 {
  573. 573 for( fp=lg->a[i];fp;fp=fp->nextArc )
  574. 574 {
  575. 575 in_degree[ fp->adjVex ]++; //编号为fp->adjVex的结点的入度加1
  576. 576 }
  577. 577 }
  578. 578 }
  579. 579 void TopSort( LGraph *lg ) //拓扑排序
  580. 580 {
  581. 581 ENode *fp;
  582. 582 int *in_degree,i,top_node;
  583. 583 Stack sta;
  584. 584 Create_S(&sta,lg->n);
  585. 585 in_degree=(int*)malloc(lg->n*sizeof(int));
  586. 586 In_Degree_Helper(in_degree,lg); //初始化该数组
  587. 587 for( i=0;i<lg->n;i++ )
  588. 588 {
  589. 589 if( in_degree[i]==0 )
  590. 590 Push_S(&sta,i);
  591. 591 }
  592. 592 while( !IsEmpty_S(&sta) )
  593. 593 {
  594. 594 Top_S(&sta,&top_node);
  595. 595 Pop_S(&sta); //这条语句不能放到for循环下面!不然栈里的元素不能被覆盖,要出大问题
  596. 596 load[cnt++]=top_node;
  597. 597 for( fp=lg->a[top_node];fp;fp=fp->nextArc )
  598. 598 {
  599. 599 in_degree[fp->adjVex]--; //更新编号为fp->adjVex的结点的入度
  600. 600 if( in_degree[fp->adjVex]==0 ) //并判断编号为fp->adjVex的结点入度是否为零,是则放入栈
  601. 601 Push_S(&sta,fp->adjVex);
  602. 602 }
  603. 603 }
  604. 604 if( cnt==lg->n )
  605. 605 {
  606. 606 printf("\n该图的拓扑排序为:");
  607. 607 for( i=0;i<lg->n;i++ )
  608. 608 printf("%3d ",load[i]);
  609. 609 }
  610. 610 else
  611. 611 {
  612. 612 printf("该图中存在有向回路,不能拓扑排序。\n");
  613. 613 }
  614. 614 }
  615. 615
  616. 616 /*
  617. 617 编写程序,实现智能交通中的最佳路径选择:设有n个地点,编号为0~n-1,m条路径的起点、终点
  618. 618 和代价由用户输入提供,采用上述邻接表为存储结构,寻找最佳路径方案(花费时间最少)
  619. 619 */
  620. 620 void DFS_PLF( LGraph *lg,Stack *s,int v,int Time ) //v为终点,起点已经装进了栈里了
  621. 621 {
  622. 622 ENode *fp; //fp为移动指针,在邻接表上移动
  623. 623 int t,i;
  624. 624 Top_S(s,&t);
  625. 625 if( t==v&&Time<Min_Time ) //终止条件:栈顶元素为终点。并且所花时间 比 以前记录的所花最少时间还要小
  626. 626 {
  627. 627 Min_Time=Time;
  628. 628 for( i=0;i<=s->top;i++ )
  629. 629 path[i]=s->element[i]; //记录当前最佳路径
  630. 630 count=i;
  631. 631 return ;
  632. 632 }
  633. 633 for( fp=lg->a[t];fp;fp=fp->nextArc )
  634. 634 {
  635. 635 if( !vis_l[fp->adjVex] ) //如果 编号为t的结点指向的 编号为fp->adjVex的结点未被访问过
  636. 636 {
  637. 637 Push_S(s,fp->adjVex); //入栈
  638. 638 vis_l[fp->adjVex]=1; //标记改变
  639. 639 DFS_PLF(lg,s,v,Time+fp->w); //时间代价要加fp->w
  640. 640 vis_l[fp->adjVex]=0; //标记回溯
  641. 641 Pop_S(s); //出栈(回溯)
  642. 642 }
  643. 643 }
  644. 644 }
  645. 645 void Prefect_Load_Find( LGraph *lg,int u,int v ) //最佳路径找查(花费时间最少),思路:深度优先搜索
  646. 646 {
  647. 647 Stack s;
  648. 648 int i;
  649. 649 Create_S(&s,lg->n);
  650. 650 Push_S(&s,u);
  651. 651 vis_l[u]=1; //标记改变,编号为u的结点已被访问
  652. 652 DFS_PLF( lg,&s,v,0 );
  653. 653 printf("从 编号为%d的起点 到 编号为%d的终点的最佳路径(花费时间最少)为:",u,v);
  654. 654 for( i=0;i<count;i++ )
  655. 655 printf("%d ",path[i]);
  656. 656 printf("\n所花费的时间为:%d\n",Min_Time);
  657. 657 }
  658. 658 void Experiment_5() //入口函数:智能交通的最佳路径的实现(花费时间最少)
  659. 659 {
  660. 660 int n; //n个地点 ,编号为0~n-1
  661. 661 int m; //m条路径
  662. 662 int u,v,w; //路径的起点u、终点v和代价w
  663. 663 int i;
  664. 664 LGraph lg;
  665. 665 Clear_Flag(); //标记清零操作
  666. 666 printf("请分别输入地点个数n和路径个数m:");
  667. 667 scanf("%d%d",&n,&m);
  668. 668 Init_L(&lg,n); //初始化邻接表,n个顶点
  669. 669 i=m;
  670. 670 while( i>0 )
  671. 671 {
  672. 672 printf("请分别输入第%d条路径的起点、终点和时间代价:",m-i+1);
  673. 673 scanf("%d%d%d",&u,&v,&w);
  674. 674 Insert_L_E(&lg,u,v,w);
  675. 675 i--;
  676. 676 }
  677. 677 printf("请输入你想找哪两个结点的最佳路径:(分别输入起点和终点)");
  678. 678 scanf("%d%d",&u,&v);
  679. 679 if( u<0||u>=lg.n||v<0||v>=lg.n||u==v ) //增强代码的健壮性,判断错误输出
  680. 680 {
  681. 681 printf("请输入正确的 你所要找查最佳路径的起点和终点!\n");
  682. 682 return ;
  683. 683 }
  684. 684 Prefect_Load_Find( &lg,u,v );
  685. 685 return ;
  686. 686 }
  687. 687
  688. 688 void M_Grapg() //入口函数:邻接矩阵的实现(额外包含矩阵打印出、入度计算)
  689. 689 {
  690. 690 int i,u,v,w,n;
  691. 691 MGraph mg;
  692. 692 printf("请输入该图的结点数:");
  693. 693 scanf("%d",&n);
  694. 694 Init_M(&mg,n,-9999);
  695. 695 printf("输入u为-999时结束。\n");
  696. 696 while( 1 )
  697. 697 {
  698. 698 scanf("%d%d%d",&u,&v,&w);
  699. 699 if( u==-999 )
  700. 700 break;
  701. 701 Insert_M_E(&mg,u,v,w);
  702. 702 }
  703. 703 Clear_Flag(); //标记清零操作
  704. 704 printf("\n矩阵的 深度遍历 结果为:\n");
  705. 705 printf("结点 ");
  706. 706 for( i=0;i<mg.n;i++ )
  707. 707 {
  708. 708 if(!vis_m[i])
  709. 709 Dfs_M(&mg,i);
  710. 710 }
  711. 711 printf("\n入度 ");
  712. 712 Print_Indegree_1(&mg); //每个结点的入度打印
  713. 713 printf("\n出度 ");
  714. 714 Print_Outdegree_1(&mg); //每个结点的出度打印
  715. 715 Print_M(&mg); //邻接矩阵打印51
  716. 716 printf("\n请分别输入需要删除的边的两端的结点值:");
  717. 717 scanf("%d%d",&u,&v);
  718. 718 if( !Delete_M_E(&mg,u,v) )
  719. 719 return ;
  720. 720 printf("删除边后矩阵的 宽度遍历 结果为:\n");
  721. 721 printf("结点 ");
  722. 722 Clear_Flag(); //标记清零操作
  723. 723 for( i=0;i<mg.n;i++ )
  724. 724 {
  725. 725 if(!vis_m[i])
  726. 726 Dfs_M(&mg,i);
  727. 727 }
  728. 728 printf("\n入度 ");
  729. 729 Print_Indegree_1(&mg); //每个结点的入度打印
  730. 730 printf("\n出度 ");
  731. 731 Print_Outdegree_1(&mg); //每个结点的出度打印
  732. 732 printf("\n");
  733. 733 Print_M(&mg); //邻接矩阵打印
  734. 734 Destroy_M(&mg);
  735. 735 }
  736. 736 void L_Graph() //入口函数:邻接表的实现(额外包含强连通图判断、邻接表打印、拓扑排序)
  737. 737 {
  738. 738 int i,u,v,w,count=0,n; //n为结点数
  739. 739 LGraph lg;
  740. 740 printf("请输入该图的结点数:");
  741. 741 scanf("%d",&n);
  742. 742 Init_L(&lg,n);
  743. 743 printf("输入u为-999时结束。\n");
  744. 744 while( 1 )
  745. 745 {
  746. 746 scanf("%d%d%d",&u,&v,&w);
  747. 747 if( u==-999 )
  748. 748 break;
  749. 749 Insert_L_E(&lg,u,v,w);
  750. 750 }
  751. 751 Clear_Flag(); //标记清零操作
  752. 752 printf("\n矩阵的 深度遍历 结果为:\n结点 ");
  753. 753 for( i=0;i<lg.n;i++ )
  754. 754 {
  755. 755 if( !vis_l[i] )
  756. 756 Dfs_L(&lg,i);
  757. 757 }
  758. 758 printf("\n入度 ");
  759. 759 Print_Indegree_2(&lg); //(邻接表)每个结点的入度打印
  760. 760 printf("\n出度 ");
  761. 761 Print_Outdegree_2(&lg); //(邻接表)每个结点的出度打印
  762. 762 Print_L(&lg); //打印邻接表
  763. 763 if( Judge_QiangLiTongTu(&lg) )
  764. 764 printf("\n该图 是 强连通图。\n");
  765. 765 else
  766. 766 printf("\n该图 不是 强连通图。\n");
  767. 767 Clear_Flag(); //标记清零操作
  768. 768 TopSort(&lg);
  769. 769 printf("\n\n请分别输入需要删除的边的两端的结点值:");
  770. 770 scanf("%d%d",&u,&v);
  771. 771 if( !Delete_L_E(&lg,u,v) )
  772. 772 return ;
  773. 773 Clear_Flag(); //标记清零操作
  774. 774 printf("删除后的图的 宽度遍历 为:\n结点 ");
  775. 775 for( i=0;i<lg.n;i++ )
  776. 776 {
  777. 777 if( !vis_l[i] )
  778. 778 Dfs_L(&lg,i);
  779. 779 }
  780. 780 printf("\n入度 ");
  781. 781 Print_Indegree_2(&lg); //(邻接表)每个结点的入度打印
  782. 782 printf("\n出度 ");
  783. 783 Print_Outdegree_2(&lg); //(邻接表)每个结点的出度打印
  784. 784 Print_L(&lg); //打印邻接表
  785. 785 Destroy_L(&lg);
  786. 786 }
  787. 787
  788. 788 void Main_Menu() //主菜单打印
  789. 789 {
  790. 790 printf("+--------------------------------------------------------------------------+\n");
  791. 791 printf("| 主菜单 |\n");
  792. 792 printf("| 1、邻接矩阵的实现(额外包含矩阵打印出、入度计算) |\n");
  793. 793 printf("| 2、邻接表的实现(额外包含强连通图判断、邻接表打印、拓扑排序) |\n");
  794. 794 printf("| 3、智能交通的最佳路径的实现(花费时间最少) |\n");
  795. 795 printf("| 0、退出 |\n");
  796. 796 printf("+--------------------------------------------------------------------------+\n");
  797. 797 }
  798. 798
  799. 799 int main()
  800. 800 {
  801. 801 int choice;
  802. 802 do
  803. 803 {
  804. 804 system("cls");
  805. 805 Main_Menu();
  806. 806 printf("请输入你的选择:\n");
  807. 807 scanf("%d",&choice);
  808. 808 switch(choice)
  809. 809 {
  810. 810 case 1: M_Grapg(); //邻接矩阵的实现(额外包含矩阵打印出、入度计算)
  811. 811 break;
  812. 812 case 2: L_Graph(); //邻接表的实现(额外包含强连通图判断、邻接表打印、拓扑排序)
  813. 813 break;
  814. 814 case 3: Experiment_5(); //智能交通的最佳路径的实现(花费时间最少)
  815. 815 break;
  816. 816 }
  817. 817 system("pause");
  818. 818 }while(choice);
  819. 819 return 0;
  820. 820 }

自己总结的关于图论的一些算法实现(C语言实现,有较详细注释,800行左右)的更多相关文章

  1. Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释

     原题链接   无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点:然后再把所有点遍历一遍 ...

  2. 魔方阵算法及C语言实现

    1 魔方阵概念 填充的,每一行.每一列.对角线之和均相等的方阵,阶数n = 3,4,5….魔方阵也称为幻方阵. 例如三阶魔方阵为: 魔方阵有什么的规律呢? 魔方阵分为奇幻方和偶幻方.而偶幻方又分为是4 ...

  3. 一个UUID生成算法的C语言实现 --- WIN32版本 .

    一个UUID生成算法的C语言实现——WIN32版本   cheungmine 2007-9-16   根据定义,UUID(Universally Unique IDentifier,也称GUID)在时 ...

  4. 无限大整数相加算法的C语言源代码

    忙里偷闲,终于完成了无限大整数相加算法的C语言代码,无限大整数相加算法的算法分析在这里. 500位的加法运行1000次,不打印结果的情况下耗时0.036秒,打印结果的情况下耗时16.285秒. 下面是 ...

  5. 数据结构算法集---C++语言实现

    //数据结构算法集---C++语言实现 //各种类都使用模版设计,可以对各种数据类型操作(整形,字符,浮点) /////////////////////////// // // // 堆栈数据结构 s ...

  6. 1164: 零起点学算法71——C语言合法标识符(存在问题)

    1164: 零起点学算法71——C语言合法标识符 Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 10 ...

  7. 【最全】经典排序算法(C语言)

    算法复杂度比较: 算法分类 一.直接插入排序 一个插入排序是另一种简单排序,它的思路是:每次从未排好的序列中选出第一个元素插入到已排好的序列中. 它的算法步骤可以大致归纳如下: 从未排好的序列中拿出首 ...

  8. PID算法(c 语言)(转)

    PID算法(c 语言)(来自老外) #include <stdio.h> #include<math.h> //定义PID 的结构体 struct _pid { int pv; ...

  9. 一个UUID生成算法的C语言实现——WIN32版本

    源: 一个UUID生成算法的C语言实现——WIN32版本

  10. 排序算法总结(C语言版)

    排序算法总结(C语言版) 1.    插入排序 1.1     直接插入排序 1.2     Shell排序 2.    交换排序 2.1     冒泡排序 2.2     快速排序 3.    选择 ...

随机推荐

  1. matlab中实现 IEEE754浮点数 与 一般十进制数之间 互相转换的方法

    ------------恢复内容开始------------ %2020/12/2 11:42:31clcformat long % IEEE754 to deca = '40800000'a = d ...

  2. PADS经验总结

    PADS经验总结 1. 快捷键z+数字,能够快速查看相应层:直接z,会显示所有层: 2. 快捷键l+数字,在走线时能够快速切换层: 3. setup->design Rules能设置线宽,DRC ...

  3. charles抓包使用

    Proxy ---> Proxy Setting ---> HTTP Proxy (设置代理的端口) 设备和代理处于同一局域网,并在设备端配置IP,端口,然后监听请求. 抓取本机的请求

  4. 第6.6节 Python动态执行小结

    一.    Python动态执行支持通过输入数据流或文件传入Python源代码串,进行编译后执行,可以通过这种方式扩展Python程序的功能: 二.    动态执行方法可能导致恶意攻击,因此使用时需要 ...

  5. css3(::before)伪元素的使用

    1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset=" ...

  6. 对象存储COS全球加速助力企业出海

    近年来,中国互联网行业迅猛发展,国内庞大的市场孕育出了许多现象级的产品,也锤炼出了非常成熟的产业链.与此同时,很多海外市场还处于萌芽期,存在着巨大的流量红利,越来越多的互联网企业开始加速"出 ...

  7. C#实例化对象的三种方式及性能对比

    前言 做项目过程中有个需求要实例化两万个对象并添加到List中,这个过程大概需要1min才能加载完(传参较多),于是开启了代码优化之旅,再此记录. 首先想到的是可能实例化比较耗时,于是开始对每种实例化 ...

  8. TMOOC 1969 开锁

    update on 2020.2.28 时隔近日重新想这道题,其实复杂度正确的解法是 可持久化 01 Trie. 考虑对于每一个 \(a[i]\),考虑能将它作为最大值的最大包容区间 \([l, r] ...

  9. AcWing 337. 扑克牌

    大型补档计划 题目链接 把状态实质相同的划分为一类... 发现花色.具体牌值的多少均不影响方案,考虑等效转化题目. 设 \(f[A][B][C][D][k]\) A 个 1 张相同,B 个 2 张相同 ...

  10. 算法——n皇后问题

    n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案. 每一种解法包含一个明确的 n 皇后问题的棋 ...