文字描述

  关于有向无环图的基础定义:

    一个无环的有向图称为有向无环图,简称DAG图(directed acycline graph)。DAG图是一类较有向树更一般的特殊有向图。

  

    举个例子说明有向无环图的应用。假如有一个表达式: ((a+b)*(b*(c+d))+(c+d)*e)*((c+d)*e), 可以用之前讨论的二叉树来表示,也可以用有向无环图来表示,如下图。显然有向无环图实现了对相同子式的共享,从而比二叉树更节省空间。

  

  关于拓扑排序的基础定义:

    由某个集合上的一个偏序得到该集合上的一个全须,这个操作称之为拓扑排序。理解起来可能有点费解,但是通俗的讲,就是如下几个操作步骤:

      1 在有向图中选一个没有前驱的顶点且输出之

      2 从图中删除该顶点和所有以它为尾的弧。

    重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况说明有向图中存在环。

  备注:AOV-网(Activity On Vertex Network)的意思是用顶点表示活动,用弧表示活动间的优先关系的有向图称为顶点表示活动的网。

示意图:

算法分析

  对n个顶点和e条弧的有向图而言,建立求各顶点的入度的时间复杂度为O(e);建零入度顶点栈的时间复杂度为O(n);在拓扑排序过程中,若有向图无环,则每个顶点进一次栈,出一次栈,入度减1的操作在while语句中总共进行e次,所以总的时间复杂度为O(n+e)。

代码实现

 //
// Created by lady on 18-12-28.
//
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTEX_NUM 20 //最大顶点数
typedef enum {DG,DN, UDG, UDN} GraphKind; //{有向图,有向网,无向图,无向网}
typedef struct ArcNode{
int adjvex; //该弧所指向的顶点的位置
struct ArcNode *nextarc; //指向下一条弧的指针
char info; //该弧相关信息的指针
}ArcNode;
typedef struct VNode{
char data[];//顶点信息
ArcNode *firstarcIN;//第一条以该顶点为弧头的弧结点,其他顶点->该结点
ArcNode *firstarcOUT;//第一条以该顶点为弧尾的弧结点,该结点->其他顶点
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct{
AdjList vertices;
int vexnum;//图的顶点数
int arcnum;//图的弧数
int kind; //图的种类标志
}ALGraph; //根据顶点信息,返回该顶点在图中的位置坐标。
int LocateVex(ALGraph *G, char data[])
{
int i = ;
for(i=; i<G->vexnum; i++){
if(!strncmp(G->vertices[i].data, data, strlen(G->vertices[i].data))){
return i;
}
}
return -;
} //利用头插法,在弧结点链表头部,插入位置v的弧结点
int InsFirst(ArcNode *L, int v)
{
if((L==NULL) || (v<)){
return -;
}
ArcNode *n = (ArcNode *)malloc(sizeof(struct ArcNode));
n->adjvex = v;
n->nextarc = L->nextarc;
L->nextarc = n;
return ;
} //采用邻接表存储方法,创建有向图
int CreateDG(ALGraph *G)
{
printf("开始创建一个有向图,请输入顶点数,弧数:");
int i = , j = , k = ;
char v1[] = {}, v2[]={};
char tmp[] = {};
G->kind = DG;
scanf("%d,%d", &G->vexnum, &G->arcnum);
for(i=; i<G->vexnum; i++){
printf("输入第%d个顶点: ", i+);
memset(G->vertices[i].data, , sizeof(G->vertices[i].data));
scanf("%s", G->vertices[i].data);
G->vertices[i].firstarcOUT = (struct ArcNode *)malloc(sizeof(struct ArcNode));
G->vertices[i].firstarcOUT->adjvex = -;
G->vertices[i].firstarcOUT->nextarc = NULL;
G->vertices[i].firstarcIN = (struct ArcNode *)malloc(sizeof(struct ArcNode));
G->vertices[i].firstarcIN->adjvex = -;
G->vertices[i].firstarcIN->nextarc = NULL;
}
for(k=; k<G->arcnum; k++)
{
printf("输入第%d条弧(顶点1, 顶点2): ", k+);
memset(tmp, , sizeof(tmp));
scanf("%s", tmp);
sscanf(tmp, "%[^','],%s[^\\n]", v1, v2);
i = LocateVex(G, v1);
j = LocateVex(G, v2);
if(i< || j<){
printf("<%s,%s> is a invalid arch!\n", v1, v2);
return -;
}
InsFirst(G->vertices[i].firstarcOUT, j);
InsFirst(G->vertices[j].firstarcIN, i);
}
return ;
} void printG(ALGraph *G)
{
printf("\n");
if(G->kind == DG){
printf("类型:有向图;顶点数 %d, 弧数 %d\n", G->vexnum, G->arcnum);
}else if(G->kind == DN){
printf("类型:有向网;顶点数 %d, 弧数 %d\n", G->vexnum, G->arcnum);
}else if(G->kind == UDG){
printf("类型:无向图;顶点数 %d, 弧数 %d\n", G->vexnum, G->arcnum);
}else if(G->kind == UDN){
printf("类型:无向网;顶点数 %d, 弧数 %d\n", G->vexnum, G->arcnum);
}
int i = ;
ArcNode *p = NULL;
printf("邻接表:\n");
for(i=; i<G->vexnum; i++){
printf("(%d,%s)\t", i,G->vertices[i].data);
p = G->vertices[i].firstarcOUT;
while(p){
if(p->adjvex >= )
printf("(%d,%s)\t", p->adjvex, G->vertices[p->adjvex].data);
p = p->nextarc;
}
printf("\n");
}
printf("逆邻接表:\n");
for(i=; i<G->vexnum; i++){
printf("(%d,%s)\t", i,G->vertices[i].data);
p = G->vertices[i].firstarcIN;
while(p){
if(p->adjvex >= )
printf("(%d,%s)\t", p->adjvex, G->vertices[p->adjvex].data);
p = p->nextarc;
}
printf("\n");
}
return;
} #define STACK_INIT_SIZE 20 //栈的初始分配量大小
#define STACK_INCREMENT 5 //栈容量不足时需新增的容量大小
typedef struct {
int *base; //指向栈底指针
int *top; //指向栈顶指针
int stacksize; //栈的当前容量大小
}SqStack; int InitStack(SqStack *s); //初始化一个栈
int StackEmpty(SqStack *s); //判断栈是否为空
int Push(SqStack *S, int *e); //入栈函数
int Pop(SqStack *S, int *e); //出栈函数 //算法各个顶点的入度,并将结果存放在indegree数组中
int FindInDegree(ALGraph *G, int indegree[])
{
printf("\n对各个顶点求入度...\n");
int i = ;
ArcNode *p = NULL;
for(i=; i<G->vexnum; i++) {
p = G->vertices[i].firstarcIN;
while (p) {
if (p->adjvex >= ) {
indegree[i] += ;
}
p = p->nextarc;
}
}
for(i=; i<G->vexnum; i++){
printf("(%d,%s)的入度为%d\n", i, G->vertices[i].data, indegree[i]);
}
return ;
} //进行拓扑排序
int ToplogicalSort(ALGraph *G)
{
int i = ;
int k = ;
int count = ;
int indegree[MAX_VERTEX_NUM] = {};
ArcNode *p = NULL;
SqStack S;
//求各个顶点的入度
FindInDegree(G, indegree);
if(InitStack(&S) <){
return -;
}
//将入度为0的顶点入栈.
for(i=; i<G->vexnum; i++){
if(!indegree[i]) {
Push(&S, &i);
}
}
printf("\n进行拓扑排序:");
while(StackEmpty(&S)){
//如果栈不为空
Pop(&S, &i);
//输入i号顶点并计数
printf("(%d,%s)\t", i, G->vertices[i].data);
++count;
for(p=G->vertices[i].firstarcOUT; p; p=p->nextarc){
//对i号顶点的每个邻接点的入度减1
k = p->adjvex;
if(!(--indegree[k])) {
//若入度减为0,则入栈
Push(&S, &k);
}
}
}
printf("\n");
if(count < G->vexnum){
printf("警告:该图有环路!!\n");
return -;
}else{
return ;
}
} int main(int argc, char *argv[])
{
ALGraph G;
//创建有向图
if(CreateDG(&G)<){
printf("创建有向图时出错!\n");
return -;
}
//打印图
printG(&G);
//进行拓扑排序
ToplogicalSort(&G);
return ;
} int InitStack(SqStack *S){
S->base = (int *) malloc(STACK_INIT_SIZE * sizeof(int));
if(!S->base){
return -;
}
S->top = S->base;
S->stacksize = STACK_INIT_SIZE;
return ;
} int StackEmpty(SqStack *s){
if(s->base == s->top){
return ;
}else{
return -;
}
} int Push(SqStack *s, int *e){
if((s->top-s->base) >= s->stacksize){
s->base = (int*)realloc(s->base, (s->stacksize+STACK_INCREMENT)*(sizeof(int)));
if(!s->base){
return -;
}
s->top = s->base + s->stacksize;
s->stacksize += STACK_INCREMENT;
}
if(e == NULL){
return -;
}else{
*s->top = *e;
}
s->top += ;
return ;
} int Pop(SqStack *s, int *e)
{
if(s->top == s->base) {
return -;
}else{
s->top -=;
*e = *s->top;
return ;
}
}

有向无环图的拓扑排序算法

代码运行

/home/lady/CLionProjects/untitled/cmake-build-debug/untitled
开始创建一个有向图,请输入顶点数,弧数:6,8
输入第1个顶点: V1
输入第2个顶点: V2
输入第3个顶点: V3
输入第4个顶点: V4
输入第5个顶点: V5
输入第6个顶点: V6
输入第1条弧(顶点1, 顶点2): V1,V2
输入第2条弧(顶点1, 顶点2): V1,V3
输入第3条弧(顶点1, 顶点2): V1,V4
输入第4条弧(顶点1, 顶点2): V3,V2
输入第5条弧(顶点1, 顶点2): V3,V5
输入第6条弧(顶点1, 顶点2): V4,V5
输入第7条弧(顶点1, 顶点2): V6,V4
输入第8条弧(顶点1, 顶点2): V6,V5 类型:有向图;顶点数 6, 弧数 8
邻接表:
(0,V1) (3,V4) (2,V3) (1,V2)
(1,V2)
(2,V3) (4,V5) (1,V2)
(3,V4) (4,V5)
(4,V5)
(5,V6) (4,V5) (3,V4)
逆邻接表:
(0,V1)
(1,V2) (2,V3) (0,V1)
(2,V3) (0,V1)
(3,V4) (5,V6) (0,V1)
(4,V5) (5,V6) (3,V4) (2,V3)
(5,V6) 对各个顶点求入度...
(0,V1)的入度为0
(1,V2)的入度为2
(2,V3)的入度为1
(3,V4)的入度为2
(4,V5)的入度为3
(5,V6)的入度为0 进行拓扑排序:(5,V6) (0,V1) (2,V3) (1,V2) (3,V4) (4,V5) Process finished with exit code 0

图->有向无环图->拓扑排序的更多相关文章

  1. 图->有向无环图->求关键路径

    文字描述 与AOV-网相对应的是AOE-网(Activity on Edge)即边表示活动的网.AOE-网是一个带权的有向无环图.其中,顶点表示事件Event,弧表示活动,权表示活动持续的时间.通常, ...

  2. 有向无环图的应用—AOV网 和 拓扑排序

    有向无环图:无环的有向图,简称 DAG (Directed Acycline Graph) 图. 一个有向图的生成树是一个有向树,一个非连通有向图的若干强连通分量生成若干有向树,这些有向数形成生成森林 ...

  3. 拓扑排序-有向无环图(DAG, Directed Acyclic Graph)

    条件: 1.每个顶点出现且只出现一次. 2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面. 有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说. 一 ...

  4. C#实现有向无环图(DAG)拓扑排序

    对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在 ...

  5. CSU 1804: 有向无环图 拓扑排序 图论

    1804: 有向无环图 Submit Page   Summary   Time Limit: 5 Sec     Memory Limit: 128 Mb     Submitted: 716    ...

  6. 大数据工作流任务调度--有向无环图(DAG)之拓扑排序

    点击上方蓝字关注DolphinScheduler(海豚调度) |作者:代立冬 |编辑:闫利帅 回顾基础知识: 图的遍历 图的遍历是指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点 ...

  7. 第十二届湖南省赛 (B - 有向无环图 )(拓扑排序+思维)好题

    Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始.点 v 结束的路径). 为了方便,点用 1,2,…,n 编号. 设 count(x,y) 表示点 x 到点 y ...

  8. 湖南省第十二届大学生计算机程序设计竞赛 B 有向无环图 拓扑DP

    1804: 有向无环图 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 187  Solved: 80[Submit][Status][Web Board ...

  9. 【拓扑】【宽搜】CSU 1084 有向无环图 (2016湖南省第十二届大学生计算机程序设计竞赛)

    题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1804 题目大意: 一个有向无环图(DAG),有N个点M条有向边(N,M<=105 ...

随机推荐

  1. web项目 在visual studio 输出窗口显示调试信息

    //始终显示 Trace.WriteLine(“要显示的调试信息”); //调试时显示 Debug.WriteLine(“要显示的调试信息”);

  2. BitBlt 函数 详解, StretchBlt、SetStretchBltMode、SetBrushOrgEx 按句柄截图、直接截取缩略图

    BitBlt 该函数对指定的源设备环境区域中的像素进行位块(bit_block)转换,以传送到目标设备环境. 函数原型 [DllImport("gdi32.dll")] publi ...

  3. Linux下逻辑地址、线性地址、物理地址详细总结

    Linux下逻辑地址.线性地址.物理地址详细总结 一.逻辑地址转线性地址      机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU(CPU中的内存管理单元)转换成物理地址 ...

  4. Linux内核剖析(四)为arm内核构建源码树

    前面说到要做linux底层开发或者编写Linux的驱动,必须建立内核源码树,之前我们提到过在本机上构建源码树—-Linux内核剖析(三),其建立的源码树是针对i686平台的,但是我么嵌入式系统用的是a ...

  5. MacOS下保护浏览器主页和默认搜索

    Windows流氓软件已被中国人玩烂了, 终于连Mac也被沦陷. 视频演示地址: http://v.youku.com/v_show/id_XMTMwMTk4MzQ0MA==.html

  6. redmine在linux上的mysql性能优化方法与问题排查方案

    iredmine的linux服务器mysql性能优化方法与问题排查方案     问题定位:   客户端工具: 1. 浏览器inspect-tool的network timing工具分析   2. 浏览 ...

  7. Vmware 虚拟机无法启动

    问题背景: 自己的电脑坏了,用的事小伙伴的电脑,安装VMware 软件,然后创建虚拟机(放在移动硬盘上).在操作虚拟主机的时候,中间不小心碰到了移动硬盘, 然后移动硬盘就掉线了.这个时候再去启动虚拟主 ...

  8. 修改Dreamweaver CC 2017 代码背景颜色

    Windows系统路径: E:\Program Files\Adobe\Dreamweaver CC\www\extensions\default\LightTheme\main.less (如果用的 ...

  9. python爬虫高级功能

    上一篇文章中我们介绍了爬虫的实现,及爬虫爬取数据的功能,这里会遇到几个问题,比方站点中robots.txt文件,里面有禁止爬取的URL.还有爬虫是否支持代理功能.及有些站点对爬虫的风控措施.设计的爬虫 ...

  10. moving-files-from-one-linux-server-to-another-using-scp

    https://www.tecmint.com/scp-commands-examples/ https://haydenjames.io/linux-securely-copy-files-usin ...