拓扑排序及其Java实现


拓扑排序是针对有向无圈图的顶点的一种排序,使得如果存在一条从A到B的路径,那么在排序中A必定在B的前面。


拓扑排序的应用场景很好理解,比如在内存中运行着很多任务,某个任务A的执行依赖于另外一个任务B,那么在A执行完之前,B一定不能被清理。而另外一些任务是没有关联的,如何来安排这些任务被清理的顺序就需要依赖拓扑排序。


一个简单的拓扑排序的方案(Kahn算法)是:先找出任意一个没有入边的顶点,然后打印该顶点,并将它及其边一起从图中剔除,然后对其余的部分继续这个操作


这个思路很简单,下面来做具体的实现。针对一个图,如下,来做拓扑排序,使用Java来实现。




需要新建三个类:节点、图、Kahn算法

具体的代码和注释如下:

package com.algorithm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

/**
 * 拓扑排序,当前方案并没有在节点类中加入过多的内容
 * 但是在图类中加入了边的集合adjaNode
 */
public class TopoSortB {

	/**
	 * 拓扑排序节点类
	 */
	private static class Node {
		public Object val;
		public int pathIn = 0; // 入链路数量
		public Node(Object val) {
			this.val = val;
		}
	}

	/**
	 * 拓扑图类
	 */
	private static 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;
		}
	}

	//Kahn算法
	private static class KahnTopo {
		private List<Node> result; // 用来存储结果集
		private Queue<Node> setOfZeroIndegree; // 用来存储入度为0的顶点
		private Graph graph;

		//构造函数,初始化
		public KahnTopo(Graph di) {
			this.graph = di;
			this.result = new ArrayList<Node>();
			this.setOfZeroIndegree = new LinkedList<Node>();
			// 对入度为0的集合进行初始化
			for(Node iterator : this.graph.vertexSet){
				if(iterator.pathIn == 0){
					this.setOfZeroIndegree.add(iterator);
				}
			}
		}

		//拓扑排序处理过程
		private void process() {
			while (!setOfZeroIndegree.isEmpty()) {
				Node v = setOfZeroIndegree.poll();
				
				// 将当前顶点添加到结果集中
				result.add(v);
				
				if(this.graph.adjaNode.keySet().isEmpty()){
					return;
				}
				
				// 遍历由v引出的所有边
				for (Node w : this.graph.adjaNode.get(v) ) {
					// 将该边从图中移除,通过减少边的数量来表示
					w.pathIn--;
					if (0 == w.pathIn) // 如果入度为0,那么加入入度为0的集合
					{
						setOfZeroIndegree.add(w);
					}
				}
				this.graph.vertexSet.remove(v);
				this.graph.adjaNode.remove(v);
			}
			
			// 如果此时图中还存在边,那么说明图中含有环路
			if (!this.graph.vertexSet.isEmpty()) {
				throw new IllegalArgumentException("Has Cycle !");
			}
		}

		//结果集
		public Iterable<Node> getResult() {
			return result;
		}
	}
	
	//测试
	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.getResult()){
			System.out.print(temp.val.toString() + "-->");
		}
	}
	
}









  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值