TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
拓扑排序简单来说就是把一个图的所有节点排序,使得每一条有向边(u,v)对应的u都排在v的前面。 拓扑排序最大的用途就是判断一个有向图是否有环。
无前趋的顶点优先的拓扑排序方法
该方法的每一步总是输出当前无前趋(即人度为零)的顶点,其抽象算法可描述为:
NonPreFirstTopSort(G){//优先输出无前趋的顶点
while(图G中有人度为0的顶点)do{
从G中选择一个人度为0的顶点v且输出之;
从G中删去v及其所有出边;
}
if(输出的顶点数目<|V(G)|)
//若此条件不成立,则表示所有顶点均已输出,排序成功。
Error("G中存在有向环,排序失败!");
}
性质
1、 拓扑排序在有向无环图中才能排出有效的序列,否则能判断该有向图有环。
2、如果输入的有向图中的点,不存在入度为0的点,则该有向图存在回路
3、如果存在的入度为0的点大于一个,则该有向图肯定不存在一个可以确定的拓扑序列但并不妨碍拓扑排序
例: 有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。
Input
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。
接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。
Output
给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;
输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
Sample Input
4 3
1 2
2 3
4 3
Sample Output
1 2 4 3
典型的拓扑排序算法
import java.util.*;
方法一:(邻接矩阵实现图存储)
public class Main {
private int n, m;
private int[] indegree;// 顶点的入度
private int[] result;// 保存最后排好序的结果
private int[][] G;// 邻接矩阵存储
private Queue<Integer> que;//存入入度为0的顶点
public Main(int n,int m,int[] indegree,int[][] G){
this.n=n;
this.m=m;
this.indegree=indegree;
this.G=G;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[][] G;
int[] indegree;
int n,m;
while (sc.hasNext()) {
n = sc.nextInt();
m = sc.nextInt();
G = new int[n + 1][n + 1];
indegree = new int[n + 1];
while (m-- > 0) {
int u = sc.nextInt();
int v = sc.nextInt();
if (G[v] == 0) {// 这个条件一定要,避免重边的情况比如a b可能出现两次的情况
indegree[v]++;
G[v] = 1;
}
}
Main ma=new Main(n,m,indegree,G);
ma.topsort();
}
}
private void topsort() {
result = new int[n + 1];
que = new PriorityQueue<Integer>();//同名次时,序号小的排前,所以用优先队列
int index = 0;
for (int i = 1; i <= n; i++) {//找出图中所有入度为O的顶点
if (indegree == 0)
que.add(i);//编号小的在前
}
while (!que.isEmpty()) {
int u = que.poll();
result[++index] = u;
for (int i = 1; i <= n; i++) {
if (G == 1) {
indegree--;//模拟删除顶点,i的入度减1
if (indegree == 0)
que.add(i);
}
}
}
// 输出
System.out.print(result[1]);
for (int i = 2; i <= n; i++) {
System.out.print(" "+result);
}
System.out.println();
}
}
import java.util.*;
方法二:拓扑排序(使用邻接表实现)
public class Main{
private int n, m;
private int[] indegree;// 顶点的入度
private int[] result;// 保存最后排好序的结果
private List<ArrayList<Integer>> G;// 邻接表。
private Queue<Integer> que;
public Main(int n,int m,int[] indegree,List<ArrayList<Integer>> G){
this.n=n;
this.m=m;
this.indegree=indegree;
this.G=G;
}
public static void main(String[] args) {
int n,m;
int[] indegree;
List<ArrayList<Integer>> G;
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
n = sc.nextInt();
m = sc.nextInt();
G = new ArrayList<ArrayList<Integer>>();
for(int i = 1;i<=n+1;i++)
G.add(new ArrayList<Integer>());//初始化邻接表
indegree = new int[n + 1];
while (m-- > 0) {
int u = sc.nextInt();
int v = sc.nextInt();
if (!G.get(u).contains(v)) {// 这个条件一定要,避免重边的情况比如a b可能出现两次的情况
indegree[v]++;
G.get(u).add(v);
}
}
Main ma=new Main(n,m,indegree,G);
ma.topsort();
}
}
private void topsort() {
result = new int[n + 1];
que = new PriorityQueue<Integer>();
int index = 0;
for (int i = 1; i <= n; i++) {
if (indegree == 0)
que.add(i);
}
while (!que.isEmpty()) {
int v = que.poll();
result[++index] = v;
for (int i : G.get(v)) {
indegree--;
if (indegree == 0)
que.add(i);
}
}
// 输出
System.out.print(result[1]);
for (int i = 2; i <= n; i++) {
System.out.print(" " + result);
}
System.out.println();
}
}
[/code]
源码下载:http://file.javaxxz.com/2014/12/4/000753250.zip |
|