图结构练习——判断给定图是否存在合法拓扑序列(dfs算法(第一个代码),邻接矩阵(前两个代码),邻接表(第三个代码))

图结构练习——判断给定图是否存在合法拓扑序列

Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^

题目描述

 给定一个有向图,判断该有向图是否存在一个合法的拓扑序列。

输入

 输入包含多组,每组格式如下。
第一行包含两个整数n,m,分别代表该有向图的顶点数和边数。(n<=10)
后面m行每行两个整数a b,表示从a到b有一条有向边。
 

输出

 若给定有向图存在合法拓扑序列,则输出YES;否则输出NO。
 

示例输入

1 0
2 2
1 2
2 1

示例输出

YES
NO

网上看到有人的思路很巧妙,忍不住就稍作修改,贴了上来:http://www.cnblogs.com/luyingfeng/archive/2013/07/29/3223090.html
第一种思路是直接判断环是否存在的思路,这种思路采用dfs算法,是非常规思路:
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 int vis[11];
 5 int m,n,u,v;
 6 int map[11][11];
 7 int dfs(int u)//深度优先搜索遍历
 8 {
 9     vis[u]=-1;//标记-1,正在访问
10     for(v=1; v<=m; v++)
11     {
12         if(map[u][v])//如果v是u的后继元素
13         {
14             if(vis[v]<0)//如果v元素是正在访问中的状态
15                 return 0;//存在自环
16             //如果存在的不是自环
17             if(!vis[v]&&!dfs(v))//v元素还没有被访问而且v的后继元素和前面的正在访问的元素构成了回路
18                 return 0;
19         }
20     }
21     //若果u的后继结点全部没有形成自回路或者是回路
22     vis[u]=1;//标记1,访问完了,删除此节点
23     return 1;
24 }
25 int toposort()
26 {
27     for(u=1; u<=m; u++)
28     {
29         if(!vis[u])//如果u节点还没有访问
30         {
31             if(!dfs(u))//如果dfs的返回值是0,强制退出排序函数toposort,表明存在环
32                 return 0;
33         }
34     }
35     return 1;//如果u从1到m全部遍历完
36 }
37 int main()
38 {
39     while(~scanf("%d %d",&m,&n)&&(m+n!=0))
40     {
41         memset(vis,0,sizeof(vis));
42         memset(map,0,sizeof(map));
43         for(int i=0; i<=n-1; i++)
44         {
45             scanf("%d %d",&u,&v);
46             map[u][v]=1;
47         }
48         if(toposort())//如果返回值是1,不存在回路,无论是自回路或者是回路
49             printf("YES
");
50         else printf("NO
");//如果返回值是0
51     }
52     return 0;
53 }
View Code
常规思路,也是书上的思路,即判断是否存在入度为0的节点,若在循环未结束的时候,就找不到入度为0的节点,则必存在环
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 int map[51][51],count[51];
 5 int flag;
 6 int u,v,vis;
 7 int main()
 8 {
 9     int m,n;
10     while(~scanf("%d %d",&m,&n)&&(n!=0||m!=0))
11     {
12         memset(map,0,sizeof(map));
13         memset(count,0,sizeof(count));//每个节点的入度初始为0;
14         vis=0;//标记变量,用于帮助最后输出
15         for(int i=0; i<=n-1; i++)
16         {
17             scanf("%d %d",&u,&v);
18             map[u][v]=1;
19             count[v]++;
20         }
21         for(int i=0; i<=m-1; i++)
22         {
23             int flag=0;
24             for(u=1; u<=m; u++)
25             {
26                 if(count[u]==0)
27                 {
28                     flag=1;//只要有入度为0的点就变为1
29                     count[u]--;//删除掉这个节点
30                     for(v=1; v<=m; v++)//凡是与该节点有关的所有结点入度去掉1;
31                     {
32                         if(map[u][v])
33                         {
34                             count[v]--;
35                         }
36                     }
37                     break;
38                 }
39             }
40             if(flag==0)//如果没有入度为0的点,就代表没有拓扑序列
41             {
42                    vis=1;
43                    break;
44             }
45         }
46         if(vis==0)
47         printf("YES
");
48         else printf("NO
");
49     }
50     return 0;
51 }
View Code

以上两种算法全部采用邻接矩阵的方法,但是采用邻接表的方法更具有一般性,这也是以上两种方法的不足之处。

 以下是用邻接表的方法:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 struct vode
 5 {
 6     int v;
 7     struct vode *next;
 8 };
 9 struct vode *f[1001];
10 int count[1001];
11 int stack[101],top=1;
12 int m,n;
13 int topsort()
14 {
15     int i,sum=0;
16     int flag;
17     while(1)
18     {
19         flag=0;
20         for(i=1;i<=m;i++)
21         {
22             if(count[i]==0)
23             {
24                 stack[top++]=i;
25                 sum++;
26                 count[i]--;//删除i节点
27                 struct vode *p;
28                 for(p=f[i];p!=NULL;p=p->next)//把i节点的邻接点的入度都减1
29                 {
30                     int t=p->v;
31                     count[t]--;
32                 }
33                 flag=1;
34             }
35             if(flag==1)break;//如果找到了入度是0的节点,从头开始再找入度是0的节点
36         }
37         if(flag==0)//循环完了之后再也找不到入度是0的节点,这时候可能会有环,也可能没有环
38         {
39             break;
40         }
41     }
42     return sum;
43 }
44 int main()
45 {
46     while(scanf("%d%d",&m,&n)!=EOF)
47     {
48         int i;
49         memset(count,0,sizeof(count));
50         for(i=0;i<=m;i++)
51             f[i]=NULL;
52         for(i=1;i<=n;i++)
53         {
54             int u,v;
55             scanf("%d%d",&u,&v);
56             count[v]++;
57             struct vode *p;
58             p=(struct vode *)malloc(sizeof(struct vode));
59             p->v=v;
60             p->next=f[u];
61             f[u]=p;
62         }
63 
64         int sum=topsort();
65         if(sum!=m)printf("NO
");
66         else printf("YES
");
67         /*
68         通过这个循环可以得到最终每个节点的入度,如果是合法的拓扑序列,最终的结果应当都是-1
69         for(i=1;i<=m;i++)
70         printf("%d ",count[i]);
71         printf("
");
72         通过下面这个循环可以得到最终一个拓扑序列(如果合法),这个序列可能只是合法序列当中的一个
73         for(i=1;i<=m;i++)
74         printf("%d ",stack[i]);
75         printf("
");
76         */
77     }
78     return 0;
79 }
View Code

简化后的代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 struct vode
 5 {
 6     int v;
 7     struct vode *next;
 8 };
 9 struct vode *f[300];
10 int m,n;
11 int rudu[300];
12 int stack[300],top;
13 void hs()
14 {
15     int i,k=0;
16     while(k=!k)
17     for(i=1;i<=m;i++)
18     if(rudu[i]==0)
19     {
20         k=0;
21         rudu[i]--;
22         stack[top++]=i;
23         struct vode *p;
24         for(p=f[i];p!=NULL;p=p->next)
25         rudu[p->v]--;
26         break;
27     }
28 }
29 int main()
30 {
31     while(scanf("%d%d",&m,&n)!=EOF)
32     {
33         memset(f,0,sizeof(f));
34         memset(rudu,0,sizeof(rudu));
35         memset(stack,0,sizeof(stack));
36         top=0;
37         int i;
38         for(i=1;i<=n;i++)
39         {
40             int u,v;
41             scanf("%d%d",&u,&v);
42             rudu[v]++;
43             struct vode *p;
44             p=(struct vode *)malloc(sizeof(struct vode));
45             p->v=v;
46             p->next=f[u];
47             f[u]=p;
48         }
49         hs();
50         /*//下面的循环可以显示出拓扑序列
51         for(i=0;i<=top-1;i++)
52         printf("%d ",stack[i]);
53         printf("
");
54         */
55         if(top==m)printf("YES
");
56         else printf("NO
");
57     }
58     return 0;
59 }
View Code

测试数据:

5 4

1 3

2 3

3 4

3 5

输出:

YES

-1 -1 -1 -1 -1

1 2 3 4 5

测试连接:http://wenku.baidu.com/view/bb32ee2e4b73f242336c5f53.html

原文地址:https://www.cnblogs.com/kuangdaoyizhimei/p/3247680.html