拓扑排序
一,邻接表(无前驱实现)
该方法的每一步总是输出当前无前趋(即入度为零)的顶点
其抽象算法可描述为:
NonPreFirstTopSort(G){//优先输出无前趋的顶点
while(G中有入度为0的顶点)
do{
从G中选择一个入度为0的顶点v且输出之;(栈顶弹出)
从G中删去v及其所有出边;(压栈)
}
if(输出的顶点数目<|V(G)|)
//若此条件不成立,则表示所有顶点均已输出,排序成功。
Error("G中存在有向环,排序失败!");
}
注意:
无前趋的顶点优先的拓扑排序算法在具体存储结构下,为便于考察每个顶点的人度,可保存各顶点当前的人度。为避免每次选入度为0的顶点时扫描整个存储空间,可设一个栈或队列暂存所有入度为零的顶点:
在开始排序前,扫描对应的存储空间,将人度为零的顶点均入栈(队)。以后每次选人度为零的顶点时,只需做出栈(队)操作即可。
二,邻接表(DFS深度优先)
当从某顶点v出发的DFS搜索完成时,v的所有后继必定均已被访问过(想像它们均已被删除),此时的v相当于是无后继的顶点,因此在DFS算法返回之前输出顶点v即可得到 DAG的逆拓扑序列。
其中第一个输出的顶点必是无后继(出度为0)的顶点,它应是拓扑序列的最后一个顶点。若希望得到的不是逆拓扑序列,同样可增加T来保存输出的顶点。若假设T是栈,并在DFSTraverse算法的开始处将T初始化,
利用DFS求拓扑序列的抽象算法可描述为:
void DFSTopSort(G,i,T)
{
//在DisTraverse中调用此算法,i是搜索的出发点,T是栈
int j;
visited[i]=TRUE; //访问i
for(所有i的邻接点j)//即<i,j>∈E(G)
if(!visited[j])
DFSTopSort(G,j,T);
//以上语句完全类似于DFS算法
Push(&T,i); //从i出发的搜索已完成,输出i
}
只要将深度优先遍历算法DFSTraverse中对DFS的调用改为对DFSTopSort的调用,即可求得拓扑序列T。其具体算法不难从上述抽象算法求精后得到。
若G是一个DAG,则用DFS遍历实现的拓扑排序与NonSuccFirstTopSort算法完全类似;但若C中存在有向环,则前者不能正常工作。
综合源码
- #include "stdio.h"
- #include "malloc.h"
- #include "stack.h"
- #define MaxSize 10
- #define Max 100
- stack<int> mystack;//调用系统的栈
- int indegree[Max];
- /*邻接表 :Adjacency list*/
- typedef struct ArcNode //边 表节点
- {
- int adjvex;//邻接点 数值
- ArcNode *next;//下一个节点
- }ArcNode ;
- typedef struct VertexNode //顶点 表节点
- {
- char vertex; //顶点表示(A,B,C)
- ArcNode *firstedge;//第一个邻接点
- }VertexNode,AdjList[MaxSize]; //为什么要写在这个地方 ????
- //vertexNode AdjList[MaxSize]; //这样为什么不对??
- typedef struct
- {
- AdjList adjlist ;//顶点表 就是竖着的一排 不能是指针么???????????? !!!!!!!!!!!
- int VertexNumber,arcNum;//图的顶点个数,边个数
- }AlGraph;
- void CreatALGraph(AlGraph *G,char a[],int n,int e) //顶点 由数组提供, 边需要用户输入
- {
- int i,k,j;
- G->VertexNumber=n;// 顶点个数
- G->arcNum=e;//边个数
- for(i=0;i<G->VertexNumber;i++)//初始化 顶点列表
- {
- G->adjlist[i].vertex=a[i];
- G->adjlist[i].firstedge=NULL;
- }
- for(k=0;k<G->arcNum;++k)//每次输入 一条边 <i,j> 将该顶点插入 i 顶点后的列链表中
- {
- printf("please input the number of edge's two vertex\n");
- scanf("%d%d",&i,&j);//有向图 f
- ArcNode *s=(ArcNode *)malloc(sizeof(ArcNode));
- s->adjvex=j; //所邻接的 顶点在顶点列表中的下标
- //接下来 将创建好的边 表节点插入 节点i 的边表的表头
- s->next=G->adjlist[i].firstedge;
- G->adjlist[i].firstedge=s;
- }
- }
- void print_Graph(AlGraph *G) //简单的打印出来
- {
- int i;
- for(i=0;i<G->VertexNumber;++i) //输出每一行
- {
- ArcNode *node= G->adjlist[i].firstedge;
- printf("%c",G->adjlist[i].vertex);//输出链表节点
- while(node)//输出后续节点
- {
- //printf("--->%d", node->adjvex);
- printf("--->%c", G->adjlist[node->adjvex].vertex);
- nodenode=node->next;
- }
- printf("\n");
- }
- }
- void topsort(AlGraph *G,int n)//通过记录入度 进行拓扑排序
- {
- int i;
- memset(indegree,0,sizeof(indegree));//初始化数组
- /*void *memset(void *s, int c, size_t n);
- memset:作用是在一段内存块中填充某个给定的值,
- 是对较大的结构体或数组进行清零操作的一种最快方法.*/
- for(i=0;i<n;++i) //初始化整个图 的入度
- {
- ArcNode *node= G->adjlist[i].firstedge;
- while(node)
- {
- indegree[node->adjvex]++;//让节点 入度加1
- nodenode=node->next;
- }
- }
- // printf("%d\n",indegree[1]);
- for(i=0;i<n;++i)//将入度为0 的元素入栈
- {
- if(indegree[i]==0)
- {
- mystack.push(i);
- }
- }
- int count=0;
- ArcNode *p;
- while(mystack.size()!=0)//当栈内 元素不为空
- {
- i=mystack.top(); //记录栈顶
- mystack.pop(); //弹出栈顶元素
- printf("%c",G->adjlist[i]);
- count++;
- p= G->adjlist[i].firstedge;
- while(p)
- {
- //int k=p->adjvex;//记录下标 方便判断
- indegree[p->adjvex]--;//让节点 入度减1
- if(indegree[p->adjvex]==0)
- mystack.push(p->adjvex);
- pp=p->next;
- }
- }
- if(count<n)
- printf("有回路\n");
- }
- void DFS_top_sort(AlGraph *G,int v,int visited[])
- {
- //int visited[v];//用来区别 顶点有没有被访问过
- int j;
- //printf("%c",G->adjlist[v].vertex);//输出顶点 (递归调用 条件下文给出)
- visited[v]=1;
- ArcNode *p=G->adjlist[v].firstedge;
- while(p!=NULL)
- {
- j=p->adjvex;//后继 的节点的下标
- if(visited[j]!=1)//后继顶点没有被访问,则递归访问
- DFS_top_sort(G,j,visited);
- pp=p->next;
- }
- mystack.push(v); //将某个节点后继访问完后 压栈
- }
- void DFS_one(AlGraph *G)
- {
- int visited[G->VertexNumber];//用来区别 顶点有没有被访问过
- int i;
- for(i=0;i<G->VertexNumber;++i)
- visited[i]=0; //标志向量初始化
- for(i=0;i<G->VertexNumber;++i)
- if(visited[i]==0) //i未访问过
- DFS_top_sort(G,i,visited);//以i为源点开始DFS搜索
- }
- void DFS_top_sort_print(AlGraph *G,stack<int> mystack)//深度优先遍历 打印
- {
- while(mystack.size())
- {
- printf("%c",G->adjlist[mystack.top()]);
- mystack.pop();//弹出不需要参数
- }
- }
- void NULL_stack(stack<int> mystack)//清空栈
- {
- while(mystack.size())
- {
- mystack.pop();//弹出不需要参数
- }
- }
- int main()
- {
- AlGraph *G=(AlGraph *)malloc(sizeof(AlGraph));
- char a[3]={'A','B','C'};
- int n=3;
- int e=2;
- printf("********************\n");
- printf("1,创建邻接表类型的图\n");
- printf("2,邻接表真实输出\n");
- printf("3,入度法拓扑排序\n");
- printf("4,深度优先遍历法拓扑排序\n");
- //printf("4,邻接表广度优先访问\n");
- printf("********************\n");
- int i;
- while(1)
- {
- scanf("%d",&i);
- switch(i)
- {
- case 1:CreatALGraph(G,a,n,e);
- printf("创建完毕请继续……\n");break;
- case 2:print_Graph(G);break;
- case 3:NULL_stack(mystack);
- topsort(G,3);
- printf("\n");break;
- case 4:NULL_stack(mystack);
- DFS_one(G);//深度优先遍历
- DFS_top_sort_print(G,mystack);
- printf("\n");break;
- case 5:break;
- case 6:break;
- case 7:break;
- case 8:break;
- case 9:break;
- }
- }
- return 0;
- }