数据结构之图的拓扑排序

我是自动化专业的应届研究生,最终拿到了tplink、华为、vivo等公司的ssp的offer,分享自己学习过的计算机基础知识(C语言+操作系统+计算机网络+linux)以及数据结构与算法的相关知识,保证看完让你有所成长。
欢迎关注我,学习资料免费分享给你哦!还有其他超多学习资源,都是我自己学习过的,经过过滤之后的资源,免去你还在因为拥有大量资源不知如何入手的纠结,让你体系化学习。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

什么是拓扑排序

拓扑排序就是任务之间有先后依赖关系,在完成一项任务之前必须完成某一项或者某几项任务,才可以完成它。比如大学的排课,都是将基础的微积分与线性代数放在第一个学年,学会这些基础之后,才可以学习复变函数,电路,大学物理等。就是对于这些有依赖关系的项目之间的排序就是拓扑排序。比如某个专业有20门课,某些课之间存在依赖关系,这些关系已知,那么如何排课呢?就是每个学期应该哪几门课?这就是拓扑排序的用处。

拓扑排序的基本思路

假设有10门课,分别以C1到C10表示。

课程代号 课程名称 先修课程
C1 程序设计基础
C2 数据结构 C1
C3 工科数学分析(上)
C4 工科数学分析(下) C3
C5 线性代数 C4
C6 算法分析与设计 C2
C7 逻辑与计算机设计基础
C8 计算机组成 C7
C9 操作系统 C7,C8
C10 计算机网络 C9

可以把每门课程看成是一个顶点,先修课程与课程之间就可以是图中的一条有向边了,根据这种表示,可以将上面的表转换为数据结构中的图。

在这里插入图片描述

如图所示,可以看到各门课程的依赖关系,那么如何实现排课,也就是拓扑排序呢?这要依靠图的一个概念——入度,什么是入度,就是在有向图中,从别的顶点指向自己本身的边的个数,如图中C2,入度就为1,因为只有一个边指向它,还有一个出度的概念,就是从这个顶点出发的边的个数,C2的出度也为1。C9的入度为2,出度为1。那么如何把顶点的入度与拓扑排序结合呢?我们排课不就是将没有预设课程的课程先排上,然后在逐层向下嘛,那么在图中,就是入度为0的顶点就是代表着没有预设课程的课程,所以只要扫描图,然后将入度为0的课程选出来,将其指向的课程的入度减1,就可以了,直到所有课程都排好。来具体分析一下整个过程。

首先假设从C1开始,将C1拿出来,然后将C2的入度减1,得到下面所示的图
在这里插入图片描述
然后继续扫描图,C3的入度为0,将C3排到课程中,C4的入度减1。

在这里插入图片描述

继续将入度为0的C7排到课表,将C9的入度减1。
在这里插入图片描述

就这样层层的递减,每一次都将入度为0的顶点排序,将其指向顶点的入度减1,直到图中的所有顶点都已经排序,这样就得到了拓扑排序的结果。当然拓扑排序的结果不唯一。比如上面在C1排序之后,面对C2,C3,C7,C8的入度均为0,你取出的顺序不同,排序的结果就不同了。按照我们上面的排序结果如图所示:

在这里插入图片描述

我们根据前面的描述可以得出如下的伪代码

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
的遍历#include #include #define max 100 //定义节点最大个数 int tag[100]; typedef char datatype; /*----------------定义边信息--------------*/ typedef struct node { int adress; // 记录节点位子 struct node *next; //指向下一条边的指针 } edgenode; /*-------------节点元素---定义类型--------------*/ typedef struct vnode { datatype element; //节点元素 edgenode *firstedge; //节点所指向的第一条边 int id; } vexternode; /*----------------定义邻接表类型--------------*/ typedef struct map { vexternode maplist[max]; //存放头结点的顺序表 int n,e; //的顶点数和边数 } linkmap; int v[100]={0}; //深度优先遍历中标记已访问的信息 int dv[100]={0}; //广度优先遍历中标记已访问的信息 /*----------------定义建立--------------*/ linkmap *create(linkmap *maps) { int chr[100][2],chh;//chr建立二元组(没权值) char c[100]; // 存放节点元素 int i,j,m,k; edgenode *p,*pre; maps=(linkmap *)malloc(sizeof(linkmap)); printf("***********************************"); printf("\n"); printf("请输入节点个数:"); printf("输入节点个数:"); scanf("%d",&maps->n); printf("请输入边的个数:"); scanf("%d",&maps->e); scanf("%c",&chh); //空格 printf("请输入节点元素:"); for(i=0;in;i++) { scanf("%c",&c[i]);//输入节点元素 scanf("%c",&chh);//空格 maps->maplist[i].element=c[i];//把节点元素存放到邻接表中 maps->maplist[i].firstedge=NULL; } printf("请输入二元组(节点与节点之间的关系)\n"); for(i=0;ie;i++) for(j=0;j<2;j++) scanf("%d",&chr[i][j]); m=0; for(i=0;in;i++) { for(k=0;me&&chr[m][0]==i;m++,k++) { p=(edgenode *)malloc(sizeof(edgenode)); p->adress=chr[m][1]; //边p保存节点位子 if(k==0) maps->maplist[i].firstedge=p; else pre->next=p; pre=p; } p->next=NULL; } return maps; } /*----------------深度优先-------------*/ void dfs(linkmap *maps,int i)//i用来指定深度优先遍历的起始值 { edgenode *pp; printf("%c",maps->maplist[i].element); v[i]=1; pp=maps->maplist[i].firstedge; while(pp) { if(!v[pp->adress]) dfs(maps,pp->adress); pp=pp->next; } } void dfsmap(linkmap *maps) { int i=0; for(i=0;in;i++) v[i]=0; for(i=0;in;i++) if(!v[i]) { dfs(maps,i); } } /*----------------广度优先-------------*/ void bfs(linkmap *map,int i) { edgenode *p; int queue[100],front,real,k; front=-1; real=-1; printf("%c",map->maplist[i].element); dv[i]=1; queue[++real]=i; while(frontmaplist[k].firstedge; while(p) { if(!dv[p->adress]) { printf("%c",map->maplist[p->adress].element); queue[++real]=p->adress; dv[p->adress]=1; } p=p->next; } } } void bfsmap(linkmap *maps) { int i=0; for(i=0;in;i++) dv[i]=0; for(i=0;in;i++) if(!dv[i]) bfs(maps,i); } /*----------------计算入度数-------------*/ void id(linkmap *maps) { int i=0; edgenode *p=maps->maplist[i].firstedge; for(i;in;i++) maps->maplist[i].id=0; for(i=0;in;i++) { p=maps->maplist[i].firstedge; while(p) { maps->maplist[p->adress].id++; p=p->next; } } } /*----------------输出各节点的入度数-------------*/ void print(linkmap *maps) { int i=0; for(i;in;i++) printf("%d",maps->maplist[i].id); } /*----------------输出拓扑排序-------------*/ int topsort(linkmap *map) { int k=0,i,j,v,tag[100];//tag用来标记是否已访问到 int queue[100];//用队列存储 int front=0,real=0; edgenode *p; for(i=0;in;i++) { tag[i]=0;//初始化标记 } for(i=0;in;i++) { if(map->maplist[i].id==0&&tag[i]==0) { queue[++real]=i;//让每一个未被访问到的且入度为0的节点进栈 tag[i]=1;//当节点进栈时,标记此节点被访问过 } } while(frontmaplist[v].element);//输出刚出栈的元素 k++;//用来统计拓扑排序输出的个数 p=map->maplist[v].firstedge; //p指向此节点的下一条边 while(p) { j=p->adress;//j记下下一条边所对应节点的位子 if(map->maplist[j].id==0&&tag[j]==0)//下一条边节点入度减一,并判断之后入度是否为零且未被访问过 { queue[++real]=j;//让每一个未被访问到的且入度为0的节点进栈 tag[j]=1;//进栈…… } p=p->next;//p指向下一条关联于该节点的边 } } return k; //k用来计算输出的个数,并判定了是否有环 } /*--------的非递归遍历-------*/ void fdg(linkmap *maps,int i) { edgenode *p,*q; linkmap *m; int stack[100]; int top=0; stack[top]=i; printf("%c ",maps->maplist[i].element); tag[i]=1; p=maps->maplist[i].firstedge; while(top>=0) { while(p) { if(tag[p->adress]!=1) printf("%c ",maps->maplist[p->adress].element); stack[++top]=p->adress; tag[p->adress]=1; q=p; p=maps->maplist[p->adress].firstedge; if(p&&tag[p->adress]==1) p=p->next; } do{ p=q; if(top==0) { p->adress=stack[top]; top--; } else p->adress=stack[--top]; p=maps->maplist[p->adress].firstedge; if(top==-1) break; while(p!=NULL) { if(tag[p->adress]==1) p=p->next; else break; }; }while(!p); } } void fdgsmap(linkmap *maps) { int i=0; for(i=0;in;i++) tag[i]=0; for(i=0;in;i++) if(!tag[i]) fdg(maps,i); } void main() { edgenode *p1; linkmap *maps; int i=0,c,num; maps=create(maps); id(maps); printf("深度优先遍历结果为:"); dfsmap(maps); printf("\n广度优先遍历结果为:"); bfsmap(maps); printf("拓扑排序结果为:"); num=topsort(maps); if(num==maps->n) printf("此拓扑排序树无环\n"); else printf("此拓扑排序树有环\n"); printf(" \n非递归深度优先遍历结果为:"); fdgsmap(maps); printf("\n"); }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值