拓扑排序(Kahn算法和基于DFS求解法)

拓扑排序是对有向无环图(DAG)进行排序,从而找到一个序列。该序列满足对于任意一对不同的顶点u,v∈V,若G中存在一条从u->v的边,则在此序列中u在v前面。

拓扑排序也可以用来判断一个有向图是否存在环。


有两种算法可以求得该序列:

1.Kahn算法。

其实就是不断的寻找有向图中没有前驱(入度为0)的顶点,将之输出。然后从有向图中删除所有以此顶点为尾的弧。重复操作,直至图空,或者找不到没有前驱的顶点为止。

该算法还可以判断有向图是否存在环(存在环的有向图肯定没有拓扑序列),通过一个count记录找出的顶点个数,如果少于N则说明存在环使剩余的顶点的入度不为0。(degree数组记录每个点的入度数)

模板代码1:

#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;
}


2.基于DFS的求解方法。

算法导论对于该种方法讲述的比较详细,由于它用的单链表存边,下面我只贴一份自己的模板代码。

思想基于:DFS时候,遇到u->v边,通过在DFS函数快退出时将结点加入到List中实现v在序列的位置始终在u的前

面。反向序列即为所求的拓扑序列。(这篇博文有整理算法导论的拓扑排序部分)

模板代码2:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
struct node
{
	int v, next;
}edge[maxn*maxn];
int head[maxn], vis[maxn];
list<int> ans; 
int n, m, no;
inline void init()
{
	no = 0;
	memset(head, -1, sizeof head);
	memset(vis, 0, sizeof vis);
}
inline void add(int u, int v)
{
	edge[no].v = v;
	edge[no].next = head[u];
	head[u] = no++;
}
void DFS(int cur)
{
	vis[cur] = 1;
	int k = head[cur];
	while(k != -1)
	{
		if(!vis[edge[k].v]) DFS(edge[k].v);
		k = edge[k].next;
	}
	ans.push_back(cur);
}
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);
	}
	for(int i = 1; i <= n; ++i)
	{
		if(!vis[i]) DFS(i);
	} 
	list<int>::reverse_iterator it;
	for(it = ans.rbegin(); it != ans.rend(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;
	return 0;
}

继续加油~

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值