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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)