拓扑排序
此代码来自于我的队友kls ,原博客:http://dlkkill.top/2017/06/15/%E6%9C%89%E5%85%B3%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F/#comment-6
拓扑排序,是一种按照一定的先后规则,来进行排序。
一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序(来自百度百科)
简单来说,拓扑排序的规则,就像一棵技能树,要想点出某个技能,就要首先点出改技能的前置技能,然后才能将改技能点出。又或者想大家熟悉的大学选课系统一样,你必须先学过前导课程,才能更深入的学习其他过程。
排序实现思想:
首先,我们可以将某种先后关系抽象成一种有向图的关系(如a在b前面,可以构建出一条有向边,由a指向b),而且必须保证该图不存在环路(否则会出现矛盾)。当构建出图以后,可以根据边的关系给点附带度数(对无向图来说,连着n条边的节点有n个度,就是顶点连着一条线段就代表一个度,这里仅对边的终点增加度数)。
图和度数建立完成后,就要开始不断搜索度数为零的点,输出该点并减少该点指向点的度数。不断循环,直至输出所有点拓扑排序完成为止。
排序代码实现
1.暴力寻求
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#define MAX 100
usingnamespace std;
void toposort(int map[MAX][MAX],int indegree[MAX],int n)
{
int i,j,k;
for(i=0;i<n;i++) //遍历n次
{
for(j=0;j<n;j++) //找出入度为0的节点
{
if(indegree[j]==0)
{
indegree[j]--;
cout<<j<<endl;
for(k=0;k<n;k++) //删除与该节点关联的边
{
if(map[j][k]==1)
{
indegree[k]--;
}
}
break;
}
}
}
}
int main(void)
{
int n,m; //n:关联的边数,m:节点数
while(scanf("%d %d",&n,&m)==2&&n!=0)
{
int i;
int x,y;
int map[MAX][MAX]; //邻接矩阵
int indegree[MAX]; //入度
memset(map,0,sizeof(map));
memset(indegree,0,sizeof(indegree));
for(i=0;i<n;i++)
{
scanf("%d %d",&x,&y);
if(!map[x][y])
{
map[x][y]=1;
indegree[y]++;
}
}
toposort(map,indegree,m);
}
return0;
}
该代码来自于博客:
**dlkkill.top
**
这份代码的主要思想就是暴力求解,弱有n个顶点,就暴力搜索n次,每次找到一个度数为零的点,然后暴力搜索该点有关的边,继续暴力。
这份代码写起来简单方便,但是时间会比较慢。
2.(队列+ 邻接矩阵)优化拓扑排序
与前一份代码类似,不过运用了数据结构进行优化。
但是代码长度较前一份长,编写时间也会较长。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
vector<int> nodes[1010];
int degress[1010];
int n;
queue<int> dequeue;
int cnt=0;
int num[1010];
void toposort()
{
while(!dequeue.empty())
{
int t=dequeue.front();
dequeue.pop();
num[cnt++]=t;
for(int i=0; i<nodes[t].size(); i++)
{
int h=nodes[t][i];
degress[h]--;
if(degress[h]==0)
dequeue.push(h);
}
}
}
int main()
{
//int n;
cin>>n;
int a,b;
for(int i=1; i<=n; i++)
{
int t;
while(scanf("%d",&t)&&t)
{
degress[t]++;
nodes[i].push_back(t);
}
}
for(int i=1; i<=n; i++)
if(!degress[i])
dequeue.push(i);
toposort();
for(int i=0; i<n; i++)
{
if(i!=0)
cout<<" ";
cout<<num[i];
}
cout<<endl;
return 0;
}
此代码用于拓扑排序入门题目:poj2367
3.dfs回溯实现拓扑排序
核心代码:
vector<int>g[N];//邻接表存储
int vis[N],topo[N],cnt;
bool dfs(int u)
{
vis[u] = -1;//-1用来表示顶点u正在访问
for(int i = 0 ; i < g[u].size() ; i ++)
{
if(vis[g[u][i]] == -1)//表示这个点进入了两次,肯定出现了环
return false;
else if(vis[g[u][i]] == 0)
{
dfs(g[u][i]);
}
}
vis[u] = 1;
topo[cnt++] = u;//放到结果数组里,输出的时候记得倒序输出,(回溯的原因)
return true;
}
bool toposort(int n)
{
memset(vis,0,sizeof(vis));
for(int i = 1 ; i <= n ; i ++)
{
if(!vis[i])
{
if(!dfs(i)) return false;//huan
}
}
return true;
}
具体详见博文:http://blog.csdn.net/acceptedxukai/article/details/6959882