26. 拓扑排序

一、什么是拓扑排序

  如果图中从 V 到 W 有一条有向路径,则 V 一定排在 W 之前。满足此条件的顶点序列称为一个 拓扑序。获得一个拓扑序的过程就是 拓扑排序。拓扑排序是对有向无圈图的顶点的一种排序。

拓扑排序

/**
 * @brief 拓扑排序
 * 
 * @param G 图
 */
void TopSort(MGraph G)
{
    int VertexSort[G->VertexNum];                                               // 拓扑排序结果
    int Degree[G->VertexNum];                                                   // 顶点的度
    int n = 0;

    VertexNode v = {0}, u = {0};

    for (int i = 0; i < G->VertexNum; i++)
    {
        Degree[i] = 0;
        VertexSort[i] = 0;
    }
  
    GetDegree(G, Degree);

    // 将入度为0的顶点加入到队列中
    PQueue PtrQ = CreateQueue();
    for (int i = 0; i < G->VertexNum; i++)
    {
        if (Degree[i] == 0)
        {
            u.Index = i;
            u.Degree = Degree[i];
            Enqueue(PtrQ, u);
        }
    }

    // 从队列中不断移除入度为0的顶点,并把它相邻顶点的入度减1,若减到0则入队
    while (PtrQ->Front != NULL)
    {
        v = Dequeue(PtrQ);
        VertexSort[n++] = v.Index;
        for (int w = GetFirstNeighbor(G, v.Index); w >= 0; w = GetNextNeighbor(G, v.Index, w))
        {
            Degree[w]--;
            u.Index = w;
            u.Degree = Degree[w];
            if (Degree[w] == 0)
            {
                Enqueue(PtrQ, u);
            }
        }
    }
  
    PrintTopSort(G, VertexSort);
}
/**
 * @brief 获取顶点的度
 * 
 * @param G 图
 * @param Degree 保存顶点的度
 */
void GetDegree(MGraph G, int Degree[])
{
    for (int i = 0; i < G->VertexNum; i++)
    {
        int degree = 0;
        for (int j = 0; j < G->VertexNum; j++)
        {
            if (G->Matrix[j][i] > 0)
            {
                degree++;
            }
        }
        Degree[i] = degree;
    }
}
/**
 * @brief 打印拓扑排序的结果
 * 
 * @param G 图
 * @param VertexSort 保存的拓扑排序的结果
 */
void PrintTopSort(MGraph G, int VertexSort[])
{
    for (int i = 0; i < G->VertexNum; i++)
    {
        printf("%c ", G->Vertex[VertexSort[i]]);
    }
}

  有关队列的内容如下:

typedef struct VertexNode
{
    int Index;
    int Degree;
} VertexNode;

typedef struct QNode
{
    VertexNode * Data;
    struct QNode * Next;
} QNode, Queue;

typedef struct PQueue
{
    QNode * Front;
    QNode * Rear;
}PQNode, * PQueue;
/**
 * @brief 创建一个空的队列
 * 
 * @return Queue 创建的空的队列
 */
PQueue CreateQueue(void)
{
    PQueue PtrQ = (PQueue)malloc(sizeof(PQNode));
    PtrQ->Front = PtrQ->Rear = NULL;
    return PtrQ;
}
/**
 * @brief 入队
 * 
 * @param PtrQ 队列
 * @param Item 要入队的元素
 */
void Enqueue(PQueue PtrQ, VertexNode Item)
{
    QNode * RearNode = malloc(sizeof(QNode));
  
    RearNode->Data = (VertexNode *)malloc(sizeof(VertexNode));
    RearNode->Data->Degree = Item.Degree;
    RearNode->Data->Index = Item.Index;

    RearNode->Next = NULL;

    if (PtrQ->Front == NULL)                                                    // 若队列为空
    {
        PtrQ->Front = RearNode;                                                 // 队列头指针指向新节点
        PtrQ->Rear = RearNode;                                                  // 队列尾指针指向新节点
    }
    else
    {
        PtrQ->Rear->Next = RearNode;                                            // 原队列尾指针指向新节点
        PtrQ->Rear = RearNode;                                                  // 队列尾指针指向新节点
    }
}
/**
 * @brief 出队
 * 
 * @param PtrQ 队列
 * @return ElementType 要出队的元素
 */
VertexNode Dequeue(PQueue PtrQ)
{
    QNode * FrontNode = NULL;
    VertexNode FrontElement = {0};

    if (PtrQ->Front == NULL)
    {
        printf("队列为空!\n");
        return FrontElement;
    }

    FrontNode = PtrQ->Front;
    if (PtrQ->Front == PtrQ->Rear)                                              // 若队列只有一个元素
    {
        PtrQ->Front = PtrQ->Rear = NULL;                                        // 删除后队列置为空
    }
    else
    {
        PtrQ->Front = PtrQ->Front->Next;
    }
  
    FrontElement = * FrontNode->Data;
    free(FrontNode);

    return FrontElement;
}
posted @   星光映梦  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示