(十七)拓扑排序算法

在代码具体实现的时候,我们还需要两个辅助的数据结构:

1、邻接表:通过结点的索引,我们能够得到这个结点的后继结点;

2、入度数组:通过结点的索引,我们能够得到指向这个结点的结点个数。

3.入度为0的队列

具体做如下:

1、在开始排序前,扫描对应的存储空间(使用邻接表),将入度为 0 的结点放入队列。

2、只要队列非空,就从队首取出入度为 0 的结点,将这个结点输出到结果集中,并且将这个结点的所有邻接结点(它指向的结点)的入度减 1,在减 1 以后,如果这个被减 1的结点的入度为 0 ,就继续入队。

一、节点、图定义 

public class Node {
	public Object val;
	public int pathIn = 0; // 入链路数量
	public Node(Object val) {
		this.val = val;
	}

}

public class Graph {

	// 图中节点的集合
			public Set<Node> vertexSet = new HashSet<Node>();
			// 相邻的节点,纪录边
			public Map<Node, Set<Node>> adjaNode = new HashMap<Node, Set<Node>>();
	 
			// 将节点加入图中
			public boolean addNode(Node start, Node end) {
				if (!vertexSet.contains(start)) {
					vertexSet.add(start);
				}
				if (!vertexSet.contains(end)) {
					vertexSet.add(end);
				}
				if (adjaNode.containsKey(start)
						&& adjaNode.get(start).contains(end)) {
					return false;
				}
				if (adjaNode.containsKey(start)) {
					adjaNode.get(start).add(end);
				} else {
					Set<Node> temp = new HashSet<Node>();
					temp.add(end);
					adjaNode.put(start, temp);
				}
				end.pathIn++;
				return true;
			}


}

二、拓扑排序

public class KahnTopo {
	
	//存放结果集
	private ArrayList<Node> result=new ArrayList<Node>();
	
	//存放0入度的节点
	private Queue<Node> setOfZeroIndregree=new LinkedList<Node>();
	
	private Graph graph;
	
	public KahnTopo(Graph graph)
	{
		this.graph=graph;
		
		//将图中入度为0的节点放入队列中
		for(Node node:this.graph.vertexSet)
		{
			if(node.pathIn==0)
				setOfZeroIndregree.add(node);
		}
	}
	//思路:先找到任何一个没有入度的定点,然后打印该节点,并将它及其边从图中删除,然后对其他部分继续这个操作
	public void process()
	{
		while(!setOfZeroIndregree.isEmpty())
		{
			Node node=setOfZeroIndregree.poll();//获取并移除
		    
			//1.加入到结果集中
			result.add(node);
			
			/*
			 * 2.因为每次删除节点要将以其为出度的边也删除,所以可能邻接表已经删光了
			 * 为什么不在删邻接表时候return呢
			 * 因为可能还存在无入度的节点
			 * 
			 * 在这个例子中看c删除后,邻接表空了,但还有f节点
			 */
			if(this.graph.adjaNode.keySet().isEmpty()){
				
				return;
			}
			
			//3.将以该边为出度的节点 入度-1
			for(Node node1:graph.adjaNode.get(node))
			{
				node1.pathIn--;
				if(node1.pathIn==0)
					setOfZeroIndregree.add(node1);
			}
			
			//3.将该节点从图顶点表中删除
			graph.vertexSet.remove(node);
			
			//4.将该节点从临界表中删除
			graph.adjaNode.remove(node);
			
		}
		
		//如果此图中还有边,则说明有环
		if(!graph.vertexSet.isEmpty())
			throw new IllegalArgumentException("has cycle");
	}
	
	
	public static void main(String[] args) {
		Node A = new Node("A");
		Node B = new Node("B");
		Node C = new Node("C");
		Node D = new Node("D");
		Node E = new Node("E");
		Node F = new Node("F");
		
		Graph graph = new Graph();
		graph.addNode(A, B);
		graph.addNode(B, C);
		graph.addNode(B, D);
		graph.addNode(D, C);
		graph.addNode(E, C);
		graph.addNode(C, F);
		
		KahnTopo topo = new KahnTopo(graph);
		topo.process();
		for(Node temp : topo.result){
			System.out.print(temp.val.toString() + "-->");
		}
	}
}

Leecode207课程表:https://leetcode-cn.com/problems/course-schedule/

posted @ 2019-07-12 18:18  测试开发分享站  阅读(164)  评论(0编辑  收藏  举报