图2----拓扑排序

拓扑排序,也就是AOV(activity on vertex),顶点的活动有先后执行顺序,用于有向无环图。

基本思路是

1 找到无入度的点,然后去掉这个点和这个点的出度边。

2 循环执行1直到再也找不到点

3 如果点的个数等于顶点数则生成一个拓扑序列,如果小的话就是有环。


邻接矩阵实现方法:

用列下标循环,如果列没有访问过,则进入行下标循环,如果该列都为0,则表示该列表示的节点没有入度,记下下标。如果该列不是全为0则计数器清0.

用刚存入的下标找到顶点存入拓扑序列,同时将该列下标表示的顶点的出度边都去掉(就是用下标表示的行全部置为0),计数器清0.

重复上面的动作直到再也找不到为止,跳出最外层循环。


邻接表实现方法:

首先定义入度数组,用下标找到位置然后循环邻接表设置入度值。

堆栈初始化,将入度为0的顶点入栈,计数器自增。

当堆栈不为空的时候循环,出栈的顶点(去掉顶点)找到连接的arc链表,链表的保存的序号对应着节点也对应着入度数组的位置(根据序号将入度数组的值自减),如果入度为0则将该顶点入栈(下标找到该顶点),计数器自增。

最后判断个数是否是顶点的个数。

代码:

package nuaa.ds;

public class Digraph {
	private CreateGraph g;
	
	private String[] vertexes;//邻接矩阵法所
	private int[][] vr;       //使用的成员变量
	
	private VertexNode[] vertexNodes;//邻接表所使用的成员变量
	
	
	/**
	 * 手动生成有向无环图测试算法
	 * A->B->E-->F-->H
	 *    ^  ^\     ^
	 *     \ | v  /
	 * C---> D->G
	 */
	public Digraph(CreateGraph g){
		this.g = g;
		switch(g){
		case AdjacencyMatrix://邻接矩阵
			this.vertexes = new String[]{"A","B","C","D","E","F","G","H"};
			vr = new int[8][8];
			//arcs都有权重
			vr[0] = new int[]{0,7,0,0,0,0,0,0};//好
			vr[1] = new int[]{0,0,0,0,3,0,0,0};
			vr[2] = new int[]{0,0,0,4,0,0,0,0};
			vr[3] = new int[]{0,2,0,0,1,0,0,0};//多
			vr[4] = new int[]{0,0,0,0,0,5,6,0};
			vr[5] = new int[]{0,0,0,0,0,0,0,2};//浪
			vr[6] = new int[]{0,0,0,0,0,0,0,4};
			vr[7] = new int[]{0,0,0,0,0,0,0,0};//费
			break;
		case AdjacencyTable:
			vertexNodes = new VertexNode[8];
			String[] temp = {"A","B","C","D","E","F","G","H"};
			for(int i=0;i<vertexNodes.length;i++){
				vertexNodes[i] = new VertexNode(temp[i]);
			}
			vertexNodes[0].arcNode = new ArcNode(1,7);
			vertexNodes[1].arcNode = new ArcNode(4,3);
			vertexNodes[2].arcNode = new ArcNode(3,4);
			vertexNodes[3].arcNode = new ArcNode(1,2);
			vertexNodes[3].arcNode.arcNode = new ArcNode(4,1);
			vertexNodes[4].arcNode = new ArcNode(5,5);
			vertexNodes[4].arcNode.arcNode = new ArcNode(6,6);
			vertexNodes[5].arcNode = new ArcNode(7,2);
			vertexNodes[6].arcNode = new ArcNode(7,4);
		}
		
	}
	
	/**
	 * AOV(activity on vertex)网,顶点表示活动的网
	 * 工程上有用,图必须为无回路的图
	 */
	public void topologicalSort(){//拓扑排序,排序结果不唯一
		this.topologicalSort(this.g);       
	};
	
	private void topologicalSort(CreateGraph g){
		switch(g){
		case AdjacencyMatrix:
			mTopologicalSort();
			break;
		case AdjacencyTable:
			tTopologicalSort();
		}
	}
	//该方法破坏了原来了邻接矩阵,应该拷贝一份再调用更合适
	private void mTopologicalSort(){
		boolean[] visited = new boolean[vr.length];
		
		int zeroNumber = 0;//判断某列是否全为0
		int chosen = 0;//找到序列下一个节点就把下标记录下来
		int count = 0;//判断序列个数,得到的图是否是有向无环图
		while(true){
			
			//寻找拓扑序列下一节点
			for(int i=0;i<vr.length;i++){//i为列下标
				if(!visited[i]){//如果该列代表元素没有被塞入拓扑序列
					for(int j=0;j<vr.length;j++){//j为行下标,循环判断某列是否为全0
						if(vr[j][i]!=0){//有非0元素表示有前序节点
							break;//跳出来
						}
						zeroNumber++;//不然就++自增
					}
				}
				if(zeroNumber==vr.length){//一列都为0
					chosen = i;//记录下这个拓扑节点
					visited[i] = true;//这个元素要被塞入拓扑序列
					break;
				}else{
					zeroNumber = 0;
				}
			}
			if(zeroNumber==vr.length){//如果找到了拓扑序列下一个节点
				visit(vertexes[chosen]);//这里直接打印下。不是很合适
				for(int i=0;i<vr.length;i++){
					vr[chosen][i] = 0;//该行全部置为0
				}
				count++;		
			}else{//找不到下一拓扑节点了
				break;
			}
			zeroNumber = 0;//计数器归0
		}
		if(count!=vr.length)
			System.out.println("--------------有环图---------------");
		
	}
	
	private void tTopologicalSort(){
		int[] indegree = this.getIndegree();
		Stack<VertexNode> stack = new Stack<VertexNode>();//要拓扑序列用Queue更合适,只是
		Stack<VertexNode> saveStack = new Stack<VertexNode>();//为了后面的关键路径算法
		int count = 0;//计数器,判断是否有环
		for(int i=0;i<indegree.length;i++){
			if(indegree[i]==0){
				stack.push(vertexNodes[i]);
				count++;
			}
			
			
		}
		while(!stack.isEmpty()){
			VertexNode vertexNode = stack.pop();
			saveStack.push(vertexNode);
			ArcNode p = vertexNode.arcNode;
			while(p!=null){
				indegree[p.serialNumber]--;
				if(indegree[p.serialNumber]==0){
					stack.push(vertexNodes[p.serialNumber]);
					count++;
				}
				
				p = p.arcNode;
			}
		}
		if(count==vertexNodes.length){
			printSeries(saveStack);
		} else{
			System.out.println("有环图");
		}
		
	}
	//邻接表求入度,入度in English 怎么说。。。。。。。。。
	private int[] getIndegree(){
		int[] indegree = new int[this.vertexNodes.length];
		for(int i=0;i<vertexNodes.length;i++){
			ArcNode p = vertexNodes[i].arcNode;
			while(p!=null){
				indegree[p.serialNumber]++;//序号表示节点在入度数组的位置
				p = p.arcNode;
			}
		}
		return indegree;
	}
	//递归打印正确的拓扑序列
	private void printSeries(Stack<VertexNode> s){
		if(s.isEmpty()){
			return;
		}
		VertexNode vertexNode = s.pop();
		printSeries(s);
		visit(vertexNode);
	}
	private void visit(String vertex){
		System.out.print(vertex+" ");
	}
	
	private void visit(VertexNode node){
		System.out.print(node.vertex+" ");
	}
	
	class VertexNode{
		String vertex;
		ArcNode arcNode;
		public VertexNode(String vertex){
			this.vertex = vertex;
		}
	}
	class ArcNode{
		int serialNumber;//存放边连着的节点在vertexNode数组里面的位置
		int weight;//权重在求最小生成树时用到,拓扑排序没有
		ArcNode arcNode;
		public ArcNode(int serialNumber,int weight){
			this.serialNumber = serialNumber;
			this.weight = weight;
		}
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值