什么是拓扑排序
拓扑排序经常用于完成有依赖关系的任务的排序。
举个例子:
一个软件工程专业的学生必须学习系列的基本课程,其中有些课程是基础课,独立于其他课程,而另一些课程必须先学完先修课程才可以开始后序课程。这个关系可以用有向图表示。
学生必须按照拓扑有序的顺序来安排学习计划,这样才能保证学习任何一门课程时其先修课程已经学过。
拓扑排序的过程
- 在有向图中找一个无前驱的顶点且输出。
- 删除该顶点指向另外顶点的弧。
- 重复以上两步,直至不存在没有前驱的点
- 若输出的顶点数小于有向图中的顶点数,则说明图中存在环。否则输出的序列即为拓扑排序。
拓扑排序的实现
需要一个辅助数组indegree[i]来存储各个顶点的入度。
#include<iostream>
#include<stdlib.h>
#include<stack>
#include<queue>
using namespace std;
#define MVNum 10//最大顶点数
#define MAXINT 32768//表示极大值
typedef int arcType;
typedef char vertexType;
typedef struct MNode{
vertexType vertex[MVNum]; //存储顶点
arcType Arc[MVNum][MVNum]; //邻接矩阵,存储边
int vexnum,arcnum;
}GraphNode,*Graph;
int indegree[MVNum]={0}; //记录各个顶点的入度
int Init(Graph &G){
G=(GraphNode *)malloc(sizeof(GraphNode));
G->vexnum=0;
G->arcnum=0;
if(G) return 1;
else cout<<"初始化出错!"<<endl;
return 0;
}
void FindPos(Graph G,char a,char b,int &pos1,int &pos2){//查找位置
int i=0;
pos1=-1;pos2=-1;
for(int i=0;i<G->vexnum;i++){
if(G->vertex[i]==a){
pos1=i;
continue;
}
else if(G->vertex[i]==b){
pos2=i;
continue;
}
}
}
void CreateDG(Graph G) //创建有向图
{
int num=0, //控制输入顶点数
pos1,pos2, //确认顶点位置
i,j;
char a,
b,
ch; //顶点
for(int i=0;i<MVNum;i++){ //初始化
for(int j=0;j<MVNum;j++){
G->Arc[i][j]=0;
}
}
cout<<"请输入顶点,以#结束"<<endl;
cin>>ch;
while(ch!='#'&&num<10){
G->vertex[num]=ch;
cin>>ch;
num++;
}
G->vexnum=num;
cout<<"请输入对应的弧(ab与ba是方向相反的边),以##结束"<<endl;
cin>>a>>b;
while(a!='#'&&b!='#'){
cout<<a<<"->"<<b<<endl;
FindPos(G,a,b,pos1,pos2);
cout<<"位置a:"<<pos1<<"位置b:"<<pos2<<endl;
if(pos1!=-1&&pos2!=-1){
G->Arc[pos1][pos2]=1;
G->arcnum++;
indegree[pos2]++;
}
cin>>a>>b;
}
}
void topo(Graph G)
{
stack<int>s;
queue<int>q;
int i,temp;
for(i=0;i<G->vexnum;i++){
if(!indegree[i]) s.push(i);
}
while(!s.empty()){
temp=s.top();
q.push(temp);
s.pop();
for(i=0;i<G->vexnum;i++){
if(G->Arc[temp][i]){
indegree[i]--;
if(indegree[i]==0) s.push(i);
}
}
}
if(q.size()<G->vexnum) printf("该有向图有回路");
else{
while(!q.empty()){
printf("%c ",G->vertex[q.front()]);
q.pop();
}
}
}
void show(Graph G){
for(int i=0;i<G->vexnum;i++){
for(int j=0;j<G->vexnum;j++){
cout<<G->Arc[i][j]<<" ";
}
cout<<endl;
}
}
int main(){
Graph G = NULL;
Init(G);
CreateDG(G);
topo(G);
return 0;
}
#include <iostream>
#include<stack>
#include<queue>
using namespace std;
#define MVNum 10 //最大顶点信息
int indegree[MVNum]={0};
typedef char VerTexType;
typedef int OtherInfo;
//----------图的邻接表存储表示-----------
typedef struct ArcNode { //边结点
int adjvex; //该边所指向顶点的位置
struct ArcNode *nextarc; //指向下一条边的指针
OtherInfo info; //和边相关的信息
}ArcNode;
typedef struct VNode {
VerTexType data; //顶点信息
ArcNode *fristarrc; //指向第一条依附该顶点的边
} VNode,AdjList[MVNum];
typedef struct {
AdjList vertices; // 邻接表
int vexnum; //图的当前顶点数
int arcnum; //图的当前边数
}ALGraph;
int LocateVex(ALGraph G,VerTexType v) {
for (int i = 0; i < G.vexnum; i++)
if (G.vertices[i].data == v)
return i;
return -1;
}
void CreateDG(ALGraph &G) { //采用邻接表表示法,创建有向图G
int i, k;
cout << "请输入总顶点数和总边数:" ; //输出总顶点数,总边数
cin >> G.vexnum >> G.arcnum;
cout << endl;
cout << "请输入顶点:" << endl;
for (i = 0 ; i < G.vexnum; i++) { //输入各点,构建表头结点
cout << "请输入第" << (i + 1) << "个顶点的名称:";
cin >> G.vertices[i].data;
G.vertices[i].fristarrc = NULL; //初始化表头结点的指针域为NULL
}
cout << endl;
cout << "请输入一条边依附的顶点:" <<endl;
for (k = 0; k < G.arcnum; k++) {
VerTexType v1, v2;
int i, j;
cout << "请输入第" << (k+1) << "条边依附的顶点";
cin >> v1 >> v2; //输入一条边依附的两个顶点
i = LocateVex(G, v1); //确定v1和v2在G中的位置,
j = LocateVex(G, v2); //即顶点在G.vertices(邻接表)中的序号
indegree[j]++;
ArcNode *p1 = new ArcNode; //生成一个新的边结点*p1
p1->adjvex = j; //邻接点序号为j
p1->nextarc = G.vertices[i].fristarrc;
G.vertices[i].fristarrc = p1; //将新节点*p1插入vi的边表
// ArcNode *p2 = new ArcNode; //生成另一个对称的新的边结点*p2
// p2->adjvex = i; //邻接点序号为i
// p2->nextarc = G.vertices[j].fristarrc;
// G.vertices[j].fristarrc = p2; //将新结点*p2插入顶点Vj的边表
}
}
void topo(ALGraph G)
{
stack<int>s;
queue<int>q;
ArcNode *p=NULL;
int i,k,temp;
for(i=0;i<G.vexnum;i++){
if(!indegree[i]) s.push(i);
}
while(!s.empty()){
temp=s.top();
q.push(temp);
s.pop();
p=G.vertices[temp].fristarrc;
while(p!=NULL){
k=p->adjvex;
indegree[k]--;
if(indegree[k]==0) s.push(k);
p=p->nextarc;
}
}
if(q.size()<G.vexnum) printf("该有向图有回路");
else{
while(!q.empty()){
printf("%c ",G.vertices[q.front()].data);
q.pop();
}
}
}
int main(){
ALGraph G;
CreateDG(G);
int i;
cout << endl;
for (i = 0; i < G.vexnum; i++) {
VNode temp = G.vertices[i]; //将G的顶点信息付给temp
ArcNode *p = temp.fristarrc; //将顶点信息temp中的边信息给p
if ( p == NULL){
cout << G.vertices[i].data;
cout << endl;
}
else {
cout << temp.data;
while (p)
{
cout << "->";
cout << p->adjvex;
p = p->nextarc;
}
// if(!p){
// cout<<"->^";
// }
}
cout << endl;
}
topo(G);
return 0;
}
从上面可以看出拓扑排序序列不一定相同,只要满足先序在后序前面即可。
返回顶部