基于邻接表的AOV,AOE。拓扑排序和关键路径

typedef struct ArcNode {
    int adjvex;  // 该边所指向的顶点的序号
    struct ArcNode *nextarc;  // 指向下一条边的指针
    int weight; // 边的权值
} ArcNode;  // 弧边的节点结构

typedef struct {
    int data;  // 顶点信息
    int inDegree;  // 顶点的入度
    ArcNode *firstarc; // 指向第一条依附该点的边的指针
} AdjList[9]; // 定点的节点结构

typedef struct {
    AdjList adjList;  //
    int vexnum, arcnum;  // 图当前的顶点数和边数
} Graph;//图的结构定义
//region AOV  AOE
/**
 * 拓扑排序 A0V网
 * AOV(有向图):用顶点表示活动,用弧边表示优先关系;图中没有环
 * 算法步骤:
 *  1.栈初始化,累加器count初始化
 *  2.扫描顶点表,将没有入度的顶点加入栈中
 *  3.当栈非空时,循环
 *      3.1.退出栈顶元素,累加器+1
 *      3.2.将退出的栈顶元素的各个邻接点的入度-1
 *      3.3.将退出后的度为0的顶点,加入栈中
 * 可以用于判断图中有没有环
 * 即 count ?= g->vexnum
 * @param g
 */
void TopoSort(Graph *g) {
    // 栈初始化,累加器count初始化
    int count = 0;
    Stack *stack = NULL;
    stack = initStack(stack);
    //扫描顶点表,将没有入度的顶点加入栈中
    for (int i = 0; i < g->vexnum; ++i) {
        if (g->adjList[i].inDegree == 0) {
            enStack(stack, g->adjList[i].data);
        }
    }
    ArcNode *arc = NULL;
    printf("拓扑排序:");
    //当栈非空时,循环
    while (!isEmptyStack(stack)) {
        // 退出栈顶元素,累加器+1
        int vex = deStack(stack);
        count++;
        printf("%2d", g->adjList[vex].data);
        //将退出的栈顶元素的各个邻接点的入度-1
        arc = g->adjList[vex].firstarc;
        while (arc) {
            g->adjList[arc->adjvex].inDegree -= 1;
            //将退出后的度为0的顶点,加入栈中
            if (g->adjList[arc->adjvex].inDegree == 0) {
                enStack(stack, g->adjList[arc->adjvex].data);
            }
            arc = arc->nextarc;
        }
    }
    if (count < g->vexnum) {
        printf("\n图中存在环");
    } else {
        printf("\n不存在环");
    }
}

/**
 * 关键路径  AOE网
 * AOE(带权的有向图):用顶点表示事件,用弧边表示活动,用权值表示活动的时间。不存在环
 * @return
 */
void KeyPath(Graph *g) {
    // 拓扑排序
    int topo[g->vexnum];
    int ve[g->vexnum]; // 事件最早发生时间数组
    int vl[g->vexnum]; // 事件最晚发生时间数组
    int count = 0;
    Stack *stack = NULL;
    stack = initStack(stack);
    //扫描顶点表,将没有入度的顶点加入栈中
    for (int i = 0; i < g->vexnum; ++i) {
        ve[i] = 0;
        if (g->adjList[i].inDegree == 0) {
            enStack(stack, g->adjList[i].data);
        }
    }

    ArcNode *arc = NULL;
    while (!isEmptyStack(stack)) {
        int vex = deStack(stack);
        topo[count++] = vex;
        arc = g->adjList[vex].firstarc;
        while (arc) {
            if ((--(g->adjList[arc->adjvex].inDegree)) == 0) {
                enStack(stack, arc->adjvex);
            }
            int start = vex; // 边的起始点
            int end = g->adjList[arc->adjvex].data;  // 边的终点
            // 更新ve数组  start + arc_weight > end
            if (ve[start] + arc->weight > ve[end]) { // 选择最大的
                ve[end] = ve[start] + arc->weight;
            }
            arc = arc->nextarc;
        }
    }

    if (count < g->vexnum) {
        printf("存在环\n");
        return;
    }
    printf("topo:");
    for (int i = 0; i < g->vexnum; ++i) {
        printf("%4d", topo[i]);
    }
    printf("\nve:");
    for (int i = 0; i < g->vexnum; ++i) {
        printf("%4d", ve[i]);
    }

    // 初始化vl数组 即结束点的ve值,这个ve值为最大值
    for (int i = 0; i < g->vexnum; ++i) {
        vl[i] = ve[count - 1];
    }
    // 计算vl数组
    while (count != 0) {
        int start = topo[--count]; // 把拓扑序列作为栈,
        arc = g->adjList[start].firstarc; // 当前顶点的邻接点头指针
        while (arc) {
            int end = arc->adjvex; // 边的终点
            //更新vl数组=min{ vl[end]-len<start, end> },len是weight
            if (vl[start] > vl[end] - arc->weight) { // 选择最小的
                vl[start] = vl[end] - arc->weight;
            }
            arc = arc->nextarc;
        }
    }
    printf("\nvl:");
    for (int i = 0; i < g->vexnum; ++i) {
        printf("%4d", vl[i]);
    }

    printf("\n关键路径:\n");
    // 计算关键路径
    for (int i = 0; i < g->vexnum; ++i) {
        arc = g->adjList[i].firstarc;
        while(arc){
            int start = g->adjList[i].data; // 起始节点
            int end = g->adjList[arc->adjvex].data;  // 终止节点
            int ee = ve[start];  // 计算活动的最早开始事件ee,与当前边起点的ve值相同
            int el = vl[end] - arc->weight;  // 计算活动的最晚开始时间,为当前边的终点的vl值-边的权值

            if(el == ee){ // 相等则是关键路径
                printf("(%d, %d)->%d\n", start, end, arc->weight);
            }
            arc = arc->nextarc;
        }
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值