算法基础之排序篇-拓扑排序

1、算法描述
   假设G=(V,E)是一个具有n个顶点的有向图,V中顶点序列vl,v2,…,vn称做一个拓扑序列(Topological Order),当且仅当该顶点序列满足下列条件:若在有向图G中存在从顶点vi到vj的一条路径,则在顶点序列中顶点vi必须排在顶点vj之前。
    操作步骤:
             (1)、在网络中选一个没有直接前驱的顶点,并输出。
             (2)、从图中删去该顶点, 同时删去所有它发出的有向边。
             (3)、重复以上步骤,直到剩余的网中不再存在没有前趋的顶点为止。
2、图例
算法基础之排序篇-拓扑排序_第1张图片
3、代码
	public static void main(String[] args) {
		TopuSort topu = new TopuSort();
		Graph graph = topu.new Graph(20);
		for (char c : "abcdefghijklmnopq".toCharArray()) {
			graph.add(c);
		}
		graph.connect(0, 2);
		graph.connect(0, 5);
		graph.connect(0, 7);
		graph.connect(0, 8);
		graph.connect(0, 10);
		graph.connect(0, 14);
		graph.connect(1, 2);
		graph.connect(2, 6);
		graph.connect(3, 12);
		graph.connect(15, 3);
		graph.connect(11, 7);
		graph.connect(4, 12);
		graph.connect(12, 10);
		graph.connect(16, 14);
		graph.initNoFatherNode();
		Node[] nodes = topu.sort(graph);
		System.out.println(Arrays.toString(nodes));
	}
	public Node[] sort(Graph graph) {
		Node[] nodes = new Node[graph.currentLength];
		int index = 0;
		int pos = 0;
		while (graph.currentLength > 0) {
			index = graph.findOneNoFatherNode(); // 找到第一个没有后继的节点
			assert index != -1 : "图中存在环";
			nodes[pos++] = graph.nodes[index]; // 放入结果中
			graph.removeNode(index); // 从图中把它删除
		}
		return nodes;
	}
	class Node {
		private final Object value;
		private int precNums = 0;
		// 后缀节点位置
		private final List<Integer> suffixs = new ArrayList<Integer>();
		public Node(Object value) {
			super();
			this.value = value;
		}
		public Object getValue() {
			return value;
		}
		@Override
		public String toString() {
			return "Node [value=" + value + "]";
		}
	}
	class Graph {
		private final Node[] nodes;
		private int currentLength;
		private final int length;
		// 存放没有前驱的位置
		private final Set<Integer> set = new HashSet<Integer>();
		public Graph(int length) {
			super();
			this.currentLength = 0;
			this.length = length;
			nodes = new Node[length];
		}
		void add(Object value) {
			assert currentLength <= nodes.length;
			nodes[currentLength++] = new Node(value);
		}
		void connect(int from, int to) {
			assert from < length;
			assert to < length;
			nodes[to].precNums++;
			nodes[from].suffixs.add(to);
		}
		void removeNode(int index) {
			for (int i : nodes[index].suffixs) {
				nodes[i].precNums--;
				if (nodes[i].precNums == 0) {
					set.add(i);
				}
			}
			nodes[index] = null;
			currentLength--;
			set.remove(index);
		}
		void initNoFatherNode() {
			for (int i = 0; i < length; i++) {
				if (null != nodes[i] && nodes[i].precNums == 0) {
					set.add(i);
				}
			}
		}

		int findOneNoFatherNode() {
			for (int j : set) {
				if (null != nodes[j] && nodes[j].precNums == 0) {
					return j;
				}
			}
			return -1;
		}
	}

PS:拓扑排序可以用来判断一个图是否有环,如果图经过拓扑排序后没有剩余的节点,那就是无环图,否则是有环图。

你可能感兴趣的:(算法基础之排序篇-拓扑排序)