• 王道数据结构 (34) 拓扑排序


    拓扑排序的实现:
    ① 从AOV⽹中选择⼀个没有前驱的顶点并输出。
    ② 从⽹中删除该顶点和所有以它为起点的有向边。
    ③ 重复①和②直到当前的AOV⽹为空或当前⽹中不存在⽆前驱的顶点为⽌。
     
     
    代码实现:
     
    #include <stdio.h>
    #include <stdlib.h>
    #define  MAX_VERTEX_NUM 20//最大顶点个数
    #define  VertexType int//顶点数据的类型
    typedef enum{false,true} bool;
    typedef struct ArcNode{
        int adjvex;//邻接点在数组中的位置下标
        struct ArcNode * nextarc;//指向下一个邻接点的指针
    }ArcNode;
    
    typedef struct VNode{
        VertexType data;//顶点的数据域
        ArcNode * firstarc;//指向邻接点的指针
    }VNode,AdjList[MAX_VERTEX_NUM];//存储各链表头结点的数组
    
    typedef struct {
        AdjList vertices;//图中顶点及各邻接点数组
        int vexnum,arcnum;//记录图中顶点数和边或弧数
    }ALGraph;
    //找到顶点对应在邻接表数组中的位置下标
    int LocateVex(ALGraph G,VertexType u){
        for (int i=0; i<G.vexnum; i++) {
            if (G.vertices[i].data==u) {
                return i;
            }
        }
        return -1;
    }
    //创建AOV网,构建邻接表
    void CreateAOV(ALGraph **G){
        *G=(ALGraph*)malloc(sizeof(ALGraph));
       
        scanf("%d,%d",&((*G)->vexnum),&((*G)->arcnum));
        for (int i=0; i<(*G)->vexnum; i++) {
            scanf("%d",&((*G)->vertices[i].data));
            (*G)->vertices[i].firstarc=NULL;
        }
        VertexType initial,end;
        for (int i=0; i<(*G)->arcnum; i++) {
            scanf("%d,%d",&initial,&end);
           
            ArcNode *p=(ArcNode*)malloc(sizeof(ArcNode));
            p->adjvex=LocateVex(*(*G), end);
            p->nextarc=NULL;
           
            int locate=LocateVex(*(*G), initial);
            p->nextarc=(*G)->vertices[locate].firstarc;
            (*G)->vertices[locate].firstarc=p;
        }
    }
    //结构体定义栈结构
    typedef struct stack{
        VertexType data;
        struct stack * next;
    }stack;
    //初始化栈结构
    void initStack(stack* *S){
        (*S)=(stack*)malloc(sizeof(stack));
        (*S)->next=NULL;
    }
    //判断链表是否为空
    bool StackEmpty(stack S){
        if (S.next==NULL) {
            return true;
        }
        return false;
    }
    //进栈,以头插法将新结点插入到链表中
    void push(stack *S,VertexType u){
        stack *p=(stack*)malloc(sizeof(stack));
        p->data=u;
        p->next=NULL;
        p->next=S->next;
        S->next=p;
    }
    //弹栈函数,删除链表首元结点的同时,释放该空间,并将该结点中的数据域通过地址传值给变量i;
    void pop(stack *S,VertexType *i){
        stack *p=S->next;
        *i=p->data;
        S->next=S->next->next;
        free(p);
    }
    //统计各顶点的入度
    void FindInDegree(ALGraph G,int indegree[]){
        //初始化数组,默认初始值全部为0
        for (int i=0; i<G.vexnum; i++) {
            indegree[i]=0;
        }
        //遍历邻接表,根据各链表中结点的数据域存储的各顶点位置下标,在indegree数组相应位置+1
        for (int i=0; i<G.vexnum; i++) {
            ArcNode *p=G.vertices[i].firstarc;
            while (p) {
                indegree[p->adjvex]++;
                p=p->nextarc;
            }
        }
    }
    void TopologicalSort(ALGraph G){
        int indegree[G.vexnum];//创建记录各顶点入度的数组
        FindInDegree(G,indegree);//统计各顶点的入度
        //建立栈结构,程序中使用的是链表
        stack *S;
        initStack(&S);
        //查找度为0的顶点,作为起始点
        for (int i=0; i<G.vexnum; i++) {
            if (!indegree[i]) {
                push(S, i);
            }
        }
        int count=0;
        //当栈为空,说明排序完成
        while (!StackEmpty(*S)) {
            int index;
            //弹栈,并记录栈中保存的顶点所在邻接表数组中的位置
            pop(S,&index);
            printf("%d",G.vertices[index].data);
            ++count;
            //依次查找跟该顶点相链接的顶点,如果初始入度为1,当删除前一个顶点后,该顶点入度为0
            for (ArcNode *p=G.vertices[index].firstarc; p; p=p->nextarc) {
                VertexType k=p->adjvex;
                if (!(--indegree[k])) {
                    //顶点入度为0,入栈
                    push(S, k);
                }
            }
        }
        //如果count值小于顶点数量,表明该有向图有环
        if (count<G.vexnum) {
            printf("该图有回路");
            return;
        }
    }
    
    int main(){
        ALGraph *G;
        CreateAOV(&G);//创建AOV网
        TopologicalSort(*G);//进行拓扑排序
        return  0;
    }
     
     
  • 相关阅读:
    JavaScript进阶系列06,事件委托
    JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数
    JavaScript进阶系列04,函数参数个数不确定情况下的解决方案
    JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象
    JavaScript进阶系列02,函数作为参数以及在数组中的应用
    JavaScript进阶系列01,函数的声明,函数参数,函数闭包
    委托、Lambda表达式、事件系列07,使用EventHandler委托
    委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别
    委托、Lambda表达式、事件系列05,Action委托与闭包
    委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理
  • 原文地址:https://www.cnblogs.com/guangzhou11/p/13632710.html
Copyright © 2020-2023  润新知