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