拓扑概念:前后顶点是一对有序的顶点。
样例构造:
V1和V6无入度顶点,说明他们可以无条件进入。
想要进入V2,必须完成先决条件,进入V1.
同理,V3的先决条件是V1.
V5的先决条件是V3,V4,V6,三个顶点先完成。
package 拓扑排序;
import java.util.ArrayList;
public class kaha {
static int dist[]=new int[7];
static int s[]=new int[7];
public static void main(String args[])
{
int N=6;
ArrayList rlist = new ArrayList();
int map[][]={
{0,0,0,0,0,0,0},
{0,0,1,1,1,0,0},
{0,0,0,0,0,0,0},
{0,0,1,0,0,1,0},
{0,0,0,0,0,1,0},
{0,0,0,0,0,0,0},
{0,0,0,0,1,1,0}
};
for(int i=1;i<=N;i++){//初始化所有点的入度
for(int j=1;j<=N;j++)
{
if(map[j][i]!=0){
dist[i]++;
}
}
}
//共有6个顶点需要检查
int tempsum=N;
while(tempsum>=1)
{
int i;
for(i=1;i<=N;i++)
{
if(s[i]!=1&&dist[i]==0)//如果入度为而且没被决策过的点,说明这个点的先决条件都完成了,可以选上这个点了
{
s[i]=1;//标记为已决策
rlist.add(i);//加入结果队列
tempsum--;//每次减去1个顶点
break;
}
}
//更新被选出的点的邻接点
for(int j=1;j<=N;j++)
{
//这里条件需要判断
//这个点已经被加入不能走回头路,有无回路。
//是i的邻接点
if(s[j]!=1&&map[i][j]!=0)
dist[j]--;//进行更新
}
}
for(Object temp:rlist)System.out.print(" "+temp);
}
}
---------------------------------------------------------1/3更新----------------------------------------------
上述的代码存在一个缺陷:不能处理非拓扑图,也就是说如果一个图存在环路,则无法处理。
试着从实际意义上去理解:如果两个顶点A和B,互相联系的话,进入顶点A的先决条件是进入顶点B,而进入顶点B的条件是进入顶点A。这样就存在矛盾了,到底是应该先做哪一个呢。所以拓扑图是不允许有回路的。
反映到代码中如何检查存在环呢?
如果一个图存在环:按照kaha算法,进行到某一部分的时候,必定会有这种情况发生:
不存在入度为0的顶点,但是n个顶点却都还没全部执行完,这个时候就可以判定图存在环路了。
如果图中存在环,那么图必定存在上述的矛盾,所以可以使用这个方法来判断回路。
package 拓扑排序;
import java.util.ArrayList;
public class kaha {
static int dist[]=new int[7];
static int s[]=new int[7];
public static void main(String args[])
{
int N=6;
ArrayList rlist = new ArrayList();
//无环拓扑图
/*int map[][]={
{0,0,0,0,0,0,0},
{0,0,1,1,1,0,0},
{0,0,0,0,0,0,0},
{0,0,1,0,0,1,0},
{0,0,0,0,0,1,0},
{0,0,0,0,0,0,0},
{0,0,0,0,1,1,0}
};*/
//有环拓扑图
int map[][]={
{0,0,0,0,0,0,0},
{0,0,1,1,1,0,0},
{0,1,0,0,0,0,0},//2-->1 由0改成1,2-->1存在一条回路。
{0,0,1,0,0,1,0},
{0,0,0,0,0,1,0},
{0,0,0,0,0,0,0},
{0,0,0,0,1,1,0}
};
for(int i=1;i<=N;i++){//初始化所有点的入度
for(int j=1;j<=N;j++)
{
if(map[j][i]!=0){
dist[i]++;
}
}
}
//共有6个顶点需要检查
int tempsum=N;
while(tempsum>=1)
{
int i;
for(i=1;i<=N;i++)
{
if(s[i]!=1&&dist[i]==0)//如果入度为而且没被决策过的点,说明这个点的先决条件都完成了,可以选上这个点了
{
s[i]=1;//标记为已决策
rlist.add(i);//加入结果队列
tempsum--;//每次减去1个顶点
break;
}
}
//判断是否能够找到入度为0的新顶点
if(i<=N)
{
//更新被选出的点的邻接点
for(int j=1;j<=N;j++)
{
//这里条件需要判断
//这个点已经被加入不能走回头路,有无回路。
//是i的邻接点
if(s[j]!=1&&map[i][j]!=0){
dist[j]--;//进行更新
}
}
}
else
{
System.out.println("此图存在环路,非拓扑图");
break;
}
}
if(tempsum==0)
for(Object temp:rlist)System.out.print(" "+temp);
}
}
----------------------------------------------2018/1/5更新--------------------------------------------------
另外一篇文章单独开出一个贴,但是关注不是很高,所性一起贴过来,给有需要童鞋参考。有任何错误请及时指出,谢谢!
使用dfs+栈,来逆序求解拓扑序列,然后再把栈中数据逆序放到另外一个栈,实现顺序输出。
过程:
把当前点加入栈
遍历并判断当前点的邻接点
是否遍历过
是否存在栈中
如果都不是,递归及需求。
如果都是,说明存在环,return。
把当前顶点出栈。
判断环路的方法如下:
邻接点如果已经被访问了且存在于栈中,说明存在环路。
当前顶点出栈放在最后的原因:
放在最后是因为要遍历该点的所有邻接点,因为该点是他的邻接点的先决条件。
递归回退的时候,证明该点的邻接点全部访问完毕且全部合法。
package 拓扑排序;
import java.util.ArrayList;
import java.util.Stack;
//dfs求逆拓扑排序
public class dfs {
static int s[]=new int[7];
static int bz;//是否需要再继续递归
static Stack fzlist = new Stack();//复制栈
static Stack rlist = new Stack();//结果栈
static int N=4;
//无环拓扑图
/*static int map[][]={
{0,0,0,0,0},
{0,0,1,1,0},
{0,0,0,0,1},
{0,0,0,0,1},
{0,0,0,0,0}
};*/
//有环拓扑图
static int map[][]={
{0,0,0,0,0},
{0,0,1,1,0},
{0,0,0,0,1},
{0,0,0,0,1},
{0,1,0,0,0}
};
public static void main(String args[]){
dfs(1);
if(bz==0)
for (;rlist.size()>=1;) {
System.out.print(" "+rlist.pop());
}
else
System.out.println("此图有环,无解");
}
public static void dfs(int n){
s[n]=1;
fzlist.push(n);
for(int i=1;i<=N;i++)
{
if(bz==0)
{
int biaoz=0;//i点是否在fzlist栈里面的标志
for (Integer x : fzlist) {
if(x==i)biaoz=1;
}
//存在环
if(map[n][i]!=0 && s[i]==1 && biaoz==1 )
{
bz=1;
return;
}
else if(map[n][i]!=0 && s[i]!=1 && biaoz==0)//说明可以放
{
dfs(i);
}
}
else//直接返回不需要再递归了
{
return;
}
}
rlist.push(fzlist.pop());
}
}