拓撲排序


@

拓撲序

有向無環圖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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM