算法分析
拓撲排序:把事情看成圖的點,把先後關係看成有向邊,問題轉化爲在圖中求一個有先後關係的排序,就是拓撲排序。拓撲排序用BFS和DFS均可實現。
如何排序?
拓撲排序需要根據點的入度和出度,一個點的入度和出度體現了這個點的先後關係。如果一個點的入度等於0,說明它是起點,是排在最前面的。如果它的出度等於0,說明排在最後面。
因爲優先級相同的數的存在,拓撲排序可能不止一種結果。
例如:1 是最高優先級 (2 , 3)是同一優先級,5是最低優先級,則拓撲序可能是1、2、3、5或1、3、2、5;
算法步驟:
① 找到所有入度爲0的點,入隊,作爲起點,誰先誰後沒有關係,如果找不到入度爲0的點,說明不是有向無環圖(DAG),不存在拓撲排序。
② 彈出隊首a, a的所有鄰居點的入度減一,把入度減爲0的鄰居點b入隊,沒有減爲0的點不能放進隊列。
③ 繼續上述操作直至隊列爲空。
拓撲排序無解的判斷:如果隊列已空,但還有點未進入隊列,那麼這些點的入度都不是0,說明不是有向無環圖,不存在拓撲排序。
有向圖的拓撲排序
BFS手寫隊列實現。
n個點m條邊的有向圖,點的編號1~n,可能存在自環和重邊,若一個由圖中所有點構成的序列A滿足:對於圖中的每條邊(x, y),x在A中都出現在y之前,則稱A是該圖的一個拓撲序列。
如果存在拓撲序列輸出拓撲序列,否則輸出-1;
數據範圍
1≤n,m≤105
輸入樣例:
3 3
1 2
2 3
1 3
輸出樣例:
1 2 3
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
int h[N], e[N], ne[N], idx;
int n, m;
int q[N], d[N];
void add(int a, int b)
{//由數據範圍知這是一個稀疏圖,用鄰接表來寫
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool topsort()
{
int hh = 0, tt = -1;
for(int i = 1; i <= n; i ++ )
{
if(d[i] == 0)//入度邊爲0
q[++ tt] = i;//起點入隊
}
while(hh <= tt)
{
int t = q[hh ++ ];//去隊頭元素
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
d[j] -- ;//入度邊數- 1
if(d[j] == 0) q[++ tt] = j;//入度邊減爲0,入隊j
}
}
return tt == n - 1;//共進了n個點
//此步用來判斷是否無解,如果tt != n - 1說明某些點不在隊列中,表示無解。
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
for(int i = 0; i < m; i ++ )//
{
int a, b;
cin >> a >> b;
add(a, b);
d[b] ++ ;//入度邊數
}
if(topsort())//存在拓撲序列
{
for(int i = 0; i < n; i ++ ) cout << q[i] << " ";
}
else cout << "-1" << endl;
}
這是通過手寫隊列來寫,當然也可以使用STL隊列或優先隊列來寫。
複雜度分析:
① 在初始化時,查找入度爲0的點,需要檢查每個邊,複雜度O(m)
② 在隊列操作中,每個點進出隊列一次,需要檢查它直接連接的所有鄰居,複雜度O(n + m)
總複雜度爲O(n + m )