拓扑排序——C/C++

1.拓扑排序介绍

拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。这样说,可能理解起来比较抽象。下面通过简单的例子进行说明!

例如,一个项目包括A、B、C、D四个子部分来完成,并且A依赖于B和D,C依赖于D。现在要制定一个计划,写出A、B、C、D的执行顺序。这时,就可以利用到拓扑排序,它就是用来确定事物发生的顺序的。

在拓扑排序中,如果存在一条从顶点A到顶点B的路径,那么在排序结果中B出现在A的后面。

2.拓扑排序算法步骤

注:顶点A没有依赖顶点,是指不存在以A为终点的边。

  1. 构造一个队列Q(queue) 和 拓扑排序的结果队列T(topological);

  2. 计算所有顶点的依赖个数(入度)

  3. 把所有没有依赖顶点的节点放入Q;

  4. 当Q内还有顶点的时候,执行下面步骤:
    4.1 从Q中取出一个顶点n(将n从Q中删掉),并放入T(将n加入到结果集中);
    4.2 使顶点n的指向邻接点m(n是起点,m是终点);
    执行以下步骤:
    去掉边<n,m>;此时m的依赖-1.
    如果m没有依赖顶点,则把m放入Q;

    直到顶点n没有邻接点,跳出4.2
    直到所有顶点的依赖都消掉,依次将Q内剩余的顶点放入T中

3.代码

1.基本定义

由邻接表实现

typedef struct eNode {
    int adjVer;					//该边的邻接点编号
    int weight;					//该边的的信息,如权值
    struct eNode* nextEdge;		//指向下一条边的指针
}EdgeNode; 						//别名,边结点的类型

typedef struct vNode {
    EdgeNode* firstEdge;		//指向第一个边结点
}VNode; 						//别名,邻接表的头结点类型

typedef struct list {
    int n;						//顶点个数
    int e;						//边数
    VNode adjList[MAXV];		//邻接表的头结点数组
}ListGraph;						//别名,完整的图邻接表类型

2.拓扑排序

void topological(ListGraph* LG){
    int i,j;
    int index = 0;              //拓扑排序结果数组的索引
    int head = 0;               //辅助队列的头
    int rear = 0;               //辅助队列的尾
    int *queue;                 //辅助队列
    int *ins;                   //记录入度个数的队列
    int *tops;                  //拓扑排序结果数组,记录每个节点的排序后的序号。
    int n = LG->n;
    EdgeNode *p;

    ins  = (int *)malloc(n*sizeof(int));  // 入度数组
    tops = (int *)malloc(n*sizeof(int));// 拓扑排序结果数组
    queue = (int *)malloc(n*sizeof(int)); // 辅助队列
    assert(ins!=NULL && tops!=NULL && queue!=NULL);
    memset(ins, 0, n*sizeof(int));
    memset(tops, 0, n*sizeof(char));
    memset(queue, 0, n*sizeof(int));

    //统计每个顶点的入度数
    for (i = 0; i < n; ++i) {
        p = LG->adjList[i].firstEdge;
        while (p != NULL){
            ins[p->adjVer]++;
            p = p->nextEdge;
        }
    }

    //将所有入度为0的顶点入队列
    for ( i = 0; i < n; ++i) {
        if (ins[i]==0){
            queue[rear++] = i;
        }
    }
    while (head != rear){                    //队列非空
        j = queue[head++];                   // 出队列。j是顶点的序号
        tops[index++] = j;                   // 将该顶点添加到tops中,tops是排序结果,index=0
        p = LG->adjList[j].firstEdge;        // 获取以该顶点为起点的出边队列
        // 将与"p"关联的节点的入度减1;
        // 若减1之后,该节点的入度为0;则将该节点添加到队列中。
        while (p != NULL){
            ins[p->adjVer]--;                // 将节点(序号为p->adjVer)的入度减1。
            if (ins[p->adjVer]==0){
                queue[rear++] = p->adjVer;   //入队
            }
            p = p->nextEdge;                //循环下一个以顶点p为起点的p->adjVer
        }
    }

    if(index != LG->n){                     //有的依赖去不掉
        printf("图中有环");           //这是检验是否使有环图的方法
        
    }else{
        printf("== TopSort: ");
        for(i = 0; i < n; i ++)
            printf("%c ", tops[i]);
        printf("\n");
    }
    free(queue);
    free(ins);
    free(tops);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Strive_LiJiaLe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值