何爲拓撲排序:
一般常見的圖是有環的,不需要考慮頂點的先後順序,而比如在一些工程中,工程的流程圖就需要考慮頂點的先後順序,比如B的前置條件是完成A,這就需要拓撲排序來判斷圖是否滿足拓撲次序,並且能夠形象的顯示各個頂點的先後順序。
拓撲排序有多種解,拓撲排序的解並不是唯一的,有些頂點的優先性是相同的。
通常我們把頂點表示活動,邊表示活動的先後關係的有向圖稱爲頂點活動網(Activity On Vertex Network),即AOV網。
拓撲排序的算法思路:
有向圖的每個頂點有不同的入度,不斷把入度爲0的頂點的去除,並去除與之相連的邊,把其相連頂點的入度減小1,不斷循環。
最終判斷是否輸出的頂點數小於原圖的頂點數,小於則不滿足拓撲次序,否則滿足拓撲次序,輸出的頂點爲一種拓撲序列。
拓撲排序所需的圖的鄰接表結構:
爲了方便去除入度爲0的頂點和邊,所以使用帶入度的圖的鄰接結構,圖的鄰接表結構如下:
#define MAXVERTEX 10 //最大頂點數,默認爲10
struct EdgeNode //邊表
{
int adjvertex; //鄰接點的下標
int weight; //權值
EdgeNode *next; //指針指向下一個鄰接點
};
typedef struct VertexNode //頂點表
{
int data; //頂點信息
int in; //頂點入度
EdgeNode *firstEdge; //邊表指針,指向邊表
}AdjList[MAXVERTEX];
typedef struct GraphAdjList
{
AdjList adjList;
int vertexesNum; //頂點數
int edgesNum; //邊數
}*GraphAdjList;
拓撲排序實現:
輔助建立了一個棧來輸出入度爲0的頂點
//拓撲排序
bool TopologicalSort(GraphAdjList GAL)
{
int i;//用於循環
int count; //存儲要輸出的頂點個數
EdgeNode *e;
int k;
//初始化用於輔助的棧
stack<int> stack; //棧用於存放入度爲0的頂點
int topNum = 0; //存儲取出的棧頂數
//將入度爲0的頂點放入棧
for (i = 0; i < GAL->vertexesNum; i++)
{
if (GAL->adjList[i].in == 0)
{
stack.push(i);
}
}
while (stack.empty() != true) //如果存在入度爲0的頂點,不斷循環
{
topNum = stack.top(); //topNum賦值爲棧頂指針
stack.pop(); //棧頂出棧
count++; //輸出個數自增1
cout << GAL->adjList[topNum].data << endl; //輸出頂點
//遍歷該頂點的鄰接表
for (e = GAL->adjList[topNum].firstEdge; e; e = e->next)
{
k = e->adjvertex;
if ((--GAL->adjList[k].in) == 0) //鄰接點自減1,並判斷是否爲0
{
stack.push(k);
}
}
}
//循環結束,判斷是否是拓撲次序
if (count<GAL->vertexesNum)
{
return false;
}
else
{
return true;
}
}
拓撲排序時間複雜度:
初始遍歷入度爲0的頂點複雜度爲O(n),後來循環中入棧出棧,複雜度爲O(e),總的時間複雜度O(n+e)。