图的进阶:拓扑排序与关键路径算法详解

图的进阶:拓扑排序与关键路径算法详解

在数据结构中,图是一种非常重要的数据结构,它广泛应用于各种领域,如网络设计、路径规划、项目管理等。本文将深入探讨图的两个重要算法:拓扑排序和关键路径算法,并通过C语言代码实例进行说明。

一、有向无环图(DAG)与拓扑排序

**有向无环图(DAG)**是一种特殊的有向图,其中不存在任何环。DAG图在描述含有公共子式的表达式、任务调度等方面具有显著优势。

拓扑排序是对DAG图的顶点进行排序,使得对于每一条有向边(u, v),顶点u在排序中都出现在顶点v之前。拓扑排序在实际应用中常用于解决任务调度、课程安排等问题。

拓扑排序的基本步骤如下:

  1. 从DAG图中选择一个没有前驱(入度为0)的顶点并输出。
  2. 从图中删除该顶点和所有以它为起点的有向边。
  3. 重复上述步骤,直到当前的DAG图为空或不存在无前驱的顶点为止。若图中仍存在顶点但无法继续排序,则说明图中存在环,无法进行拓扑排序。

以下是一个用C语言实现拓扑排序的示例代码:

#include 
#include 

#define MAX_VERTICES 100

typedef struct {
    int graph[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵
    int indegree[MAX_VERTICES]; // 入度数组
    int vertex_num, edge_num; // 顶点数和边数
} Graph;

// 初始化入度数组
void InitIndegree(Graph *G) {
    for (int i = 0; i < G->vertex_num; i++) {
        G->indegree[i] = 0;
    }
    for (int i = 0; i < G->vertex_num; i++) {
        for (int j = 0; j < G->vertex_num; j++) {
            if (G->graph[i][j] == 1) {
                G->indegree[j]++;
            }
        }
    }
}

// 拓扑排序
int TopologicalSort(Graph *G) {
    int queue[MAX_VERTICES], front = 0, rear = -1;
    int count = 0; // 已输出的顶点数

    // 将所有入度为0的顶点入队
    for (int i = 0; i < G->vertex_num; i++) {
        if (G->indegree[i] == 0) {
            queue[++rear] = i;
        }
    }

    // 拓扑排序
    while (front <= rear) {
        int cur_vertex = queue[front++];
        printf("%d ", cur_vertex); // 输出顶点
        count++;

        // 遍历以当前顶点为起点的所有边
        for (int i = 0; i < G->vertex_num; i++) {
            if (G->graph[cur_vertex][i] == 1) {
                G->indegree[i]--;
                if (G->indegree[i] == 0) {
                    queue[++rear] = i;
                }
            }
        }
    }

    if (count == G->vertex_num) {
        // 如果已输出的顶点数等于总顶点数,说明拓扑排序成功
        return 1;
    } else {
        // 否则说明存在环,拓扑排序失败
        return 0;
    }
}

int main() {
    Graph G;
    G.vertex_num = 6;
    G.edge_num = 6;

    // 构造图的邻接矩阵
    for (int i = 0; i < G.vertex_num; i++) {
        for (int j = 0; j < G.vertex_num; j++) {
            G.graph[i][j] = 0;
        }
    }
    G.graph[0][1] = 1;
    G.graph[0][3] = 1;
    G.graph[1][3] = 1;
    G.graph[1][2] = 1;
    G.graph[3][4] = 1;
    G.graph[2][4] = 1;
    // 注意:这里G.graph[3][2] = 1;不应该存在,因为它会形成一个环

    InitIndegree(&G);
    if (TopologicalSort(&G)) {
        printf("拓扑排序成功!\n");
    } else {
        printf("图中存在环,拓扑排序失败!\n");
    }

    return 0;
}
二、关键路径算法

**关键路径法(CPM)**是项目管理中的一个重要概念,用于找出决定项目最短完成时间的关键活动。在有向图中,关键路径是从起点到终点的具有最大路径长度的路径,它决定了项目的最短完成时间。

关键路径算法的基本步骤如下:

  1. 计算最早开始时间和最早完成时间:从源点(开始顶点)出发,按拓扑排序计算每个顶点的最早开始时间ve()和最早完成时间。
  2. 计算最迟开始时间和最迟完成时间:从汇点(结束顶点)出发,按逆拓扑排序计算每个顶点的最迟开始时间vl()和最迟完成时间。
  3. 计算活动的差额:对于每个活动,计算其最早开始时间与最迟开始时间的差额d()。
  4. 找出关键路径:所有d()=0的活动构成关键路径。

以下是一个用C语言实现关键路径算法的示例代码(部分):

// 假设图的邻接表和相关数据结构已经定义,并且图的构建已经完成
// 这里只展示关键路径算法的核心部分

// 计算最早开始时间和最早完成时间
void CalculateEarliestTimes(Graph *G, int *ve) {
    // 初始化源点的最早开始时间为0
    ve[0] = 0;

    // 使用拓扑排序计算其余顶点的最早开始时间
    // 拓扑排序的代码已经在上面给出,这里假设已经得到了拓扑排序序列
    // 遍历拓扑排序序列,更新每个顶点的最早开始时间
    // ...(具体实现省略,可参考相关算法资料)
}

// 计算最迟开始时间和最迟完成时间
void CalculateLatestTimes(Graph *G, int *ve, int *vl) {
    // 初始化汇点的最迟完成时间为ve[汇点] + 汇点出度所代表的活动时间(如果有的话)
    // 这里假设汇点的编号为G->vertex_num - 1
    vl[G->vertex_num - 1] = ve[G->vertex_num - 1]; // 或者加上汇点出边的权重和

    // 使用逆拓扑排序计算其余顶点的最迟开始时间和最迟完成时间
    // 逆拓扑排序可以通过对拓扑排序序列进行反转得到
    // ...(具体实现省略,可参考相关算法资料)
}

// 找出关键路径
void FindCriticalPath(Graph *G, int *ve, int *vl) {
    // 遍历图中的每条边,计算活动的差额d()
    // 如果d()=0,则该活动属于关键路径
    // ...(具体实现省略,可参考相关算法资料)
}

int main() {
    // ...(图的构建和初始化代码省略)

    int ve[MAX_VERTICES], vl[MAX_VERTICES];

    // 计算最早开始时间和最早完成时间
    CalculateEarliestTimes(G, ve);

    // 计算最迟开始时间和最迟完成时间
    CalculateLatestTimes(G, ve, vl);

    // 找出关键路径
    FindCriticalPath(G, ve, vl);

    return 0;
}

注意:由于篇幅限制,上述关键路径算法中的CalculateEarliestTimesCalculateLatestTimesFindCriticalPath函数的具体实现已经省略。在实际应用中,需要根据具体的图结构和需求进行完整的实现。

总结

本文详细介绍了图的两个重要算法:拓扑排序和关键路径算法。拓扑排序用于对有向无环图的顶点进行排序,而关键路径算法则用于找出决定项目最短完成时间的关键活动。通过C语言代码实例,读者可以更好地理解和掌握这两个算法的实现和应用。希望本文能对读者在数据结构的学习和实践中有所帮助。

你可能感兴趣的:(数据结构与算法,C/C++,算法,图论,图搜索,数据结构,c语言)