拓扑排序-java实现

拓扑排序

拓扑概念:前后顶点是一对有序的顶点。

样例构造:

 

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<Integer> fzlist = new Stack<Integer>();//复制栈
	static Stack<Integer> rlist = new Stack<Integer>();//结果栈
	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());
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值