@
拓撲序
有向無環圖DAG,對每一個頂點給一個編號,第i號頂點叫做v~i~,那么存在頂點v~i~到頂點v~j~的邊時,就有i<j成立,這樣的編號方式叫做拓撲序。
拓撲排序
如果把圖(a)中的頂點按照拓撲序從右到左排列,那么所有的邊都是從左指向右的。求解拓撲序的算法叫做拓撲排序。
DFS算法
使用棧以使結果為正向排序,由於每個頂點和每條邊都只訪問了一次,因此復雜度時O(|V|+|E|).
已知為DAG的情況
對每一個節點設置屬性visited為0或1,未訪問過為0,訪問過為1.
dfs(u)
其中n為節點,不斷深入遞歸,當其所有子節點都已被訪問過之后才會退出。
所以可以知道最先被訪問的必定是拓撲序最大的,則先將其push
至棧中,最后從棧中取出時即為正序。
#include <iostream>
#include <stack>
using namespace std;
struct Edge {
int to, next;
};
const int maxn = 10010;
int head[maxn] = {};
int n, m, cnt = 1;
bool vis[maxn] = {};
Edge edge[maxn];
stack<int> S;
void add(int u, int v)
{
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void dfs(int u)
{
vis[u] = true;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (!vis[v]) dfs(v);
}
S.push(u);
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
add(u, v);
}
for (int i = 1; i <= n; ++i) {
if (vis[i] == 0) dfs(i);
}
while (!S.empty()) {
cout << S.top() << ' ';
S.pop();
}
}
判環
那么,如何判環呢?判環只是在dfs
函數上稍做些修改,其中最主要的是vis
數組的含義有所擴展,以及對下一結點進行dfs
的條件判斷。
不判環的拓撲排序,vis
只代表某一結點有沒有被放問過,而現在,vis
有三個值,-1,0,1。
-1代表已訪問過,但不是從當前系列dfs
訪問來的,0代表未訪問過,1代表訪問過,且是當前系列訪問過的(意味着有環,如u->v
, v->t
, t->u
)
bool dfs(int u)
{
vis[u] = 1;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (vis[v] == 1) return false;
if (vis[v] == 0 && !dfs(v)) return false;
}
vis[u] = -1;
S.push(u);
return true;
}
Kahn算法
其實就是不斷的尋找有向圖中沒有前驅(入度為0)的頂點,將之輸出。然后從有向圖中刪除所有以此頂點為尾的弧。重復操作,直至圖空,或者找不到沒有前驅的頂點為止。
該算法還可以判斷有向圖是否存在環(存在環的有向圖肯定沒有拓撲序列),通過一個count記錄找出的頂點個數,如果少於N則說明存在環使剩余的頂點的入度不為0。(degree數組記錄每個點的入度數)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
struct node
{
int v, next;
}edge[maxn*maxn];
int degree[maxn], head[maxn];
queue<int> q;
list<int> ans;
int n, m, no;
inline void init()
{
no = 0;
while(!q.empty()) q.pop();
memset(degree, 0, sizeof degree);
memset(head, -1, sizeof head);
}
inline void add(int u, int v)
{
edge[no].v = v;
edge[no].next = head[u];
head[u] = no++;
}
int Kahn()
{
int count = 0;
while(!q.empty())
{
int tp = q.front(); q.pop();
++count; ans.push_back(tp); //加入鏈表中,加入數組或者vector或者queue都無所謂
int k = head[tp];
while(k != -1)
{
--degree[edge[k].v];
if(!degree[edge[k].v]) q.push(edge[k].v);
k = edge[k].next;
}
}
if(count == n) return 1;
return 0;
}
int main()
{
int u, v;
scanf("%d %d", &n, &m); init();
for(int i = 1; i <= m; ++i)
{
scanf("%d %d", &u, &v);
add(u, v); ++degree[v];
}
for(int i = 1; i <= n; ++i)
{
if(!degree[i]) q.push(i);
}
Kahn();
list<int>::iterator it;
for(it = ans.begin(); it != ans.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
return 0;
}