拓扑排序

@

拓扑序

有向无环图DAG,对每一个顶点给一个编号,第i号顶点叫做vi,那么存在顶点vi到顶点vj的边时,就有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;
}
posted @ 2019-03-31 16:46  woria  阅读(1236)  评论(0编辑  收藏  举报