利用栈实现拓扑排序


一、什么是拓扑排序?

对一个有向图,设G=(V,E),V表示的是顶点集合,E表示的是顶点间的边关系,若 Vi -> Vj存在路径,则Vi一定排在Vj之前,则我们称这样的顶点序列为拓扑序列,使之构成拓扑序列的过程叫做拓扑排序。
由上述可知,拓扑序列有几点特点:

  1. 有向图
  2. 无环

二、实现思想

由第一章节可知,根据拓扑排序思想,排在前列的都是没有指向的,所以我们首先要找入度为0的顶点,因为入度为0的顶点,不存在指向它的顶点,也就意味着没有排在它前面的顶点。

三、实现步骤

  1. 初始化图
  2. 初始化所有顶点的入度0
  3. 统计所有顶点的入度
  4. 找到入度为0的顶点入栈
  5. 栈顶元素出栈,找到栈顶元素指向其他顶点的边,并减少该顶点入度1,若此时如果该顶点入度为0的话就入栈。
  6. 循环步骤5,直至栈元素全部出栈。

四、代码实现

public class Test {

	/**
	 * 端点矩阵  图
	 */
	private static final int[][] GRAPH;


	/**
	 * 判断两节点是否存在边
	 */
	private static final int NOT_DIRECT_FLAG = -1;
	/**
	 * 判断两节点存在边
	 */
	private static final int DIRECT_FLAG = 1;


	static {
		GRAPH = new int[14][14];
		initGraph();

	}


	/**
	 * 初始化图表
	 */
	public static void initGraph() {
		for (int i = 0; i < GRAPH.length; i++) {
			for (int j = 0; j < GRAPH[i].length; j++) {
				if (i == j) {
					GRAPH[i][j] = 0;
				} else {
					GRAPH[i][j] = NOT_DIRECT_FLAG;
				}

			}
		}
		GRAPH[0][4] = DIRECT_FLAG;
		GRAPH[0][5] = DIRECT_FLAG;
		GRAPH[0][11] = DIRECT_FLAG;
		GRAPH[1][4] = DIRECT_FLAG;
		GRAPH[1][8] = DIRECT_FLAG;
		GRAPH[1][2] = DIRECT_FLAG;
		GRAPH[2][5] = DIRECT_FLAG;
		GRAPH[2][6] = DIRECT_FLAG;
		GRAPH[2][9] = DIRECT_FLAG;
		GRAPH[3][2] = DIRECT_FLAG;
		GRAPH[3][13] = DIRECT_FLAG;
		GRAPH[4][7] = DIRECT_FLAG;
		GRAPH[5][8] = DIRECT_FLAG;
		GRAPH[5][12] = DIRECT_FLAG;
		GRAPH[8][7] = DIRECT_FLAG;
		GRAPH[9][10] = DIRECT_FLAG;
		GRAPH[10][13] = DIRECT_FLAG;
		GRAPH[12][9] = DIRECT_FLAG;

	}

	public static void main(String[] args) throws IllegalAccessException {
		Map<Integer, Integer> inDegreeMap = initInDegree();
		calculateInDegree(inDegreeMap);
		Stack<Integer> stack = initStack(inDegreeMap);
		topologicalSort(inDegreeMap, stack);

	}

	public static Map<Integer, Integer> initInDegree() {
		Map<Integer, Integer> map = new HashMap<>(32);
		map.put(0, 0);
		map.put(1, 0);
		map.put(2, 0);
		map.put(3, 0);
		map.put(4, 0);
		map.put(5, 0);
		map.put(6, 0);
		map.put(7, 0);
		map.put(8, 0);
		map.put(9, 0);
		map.put(10, 0);
		map.put(11, 0);
		map.put(12, 0);
		map.put(13, 0);

		return map;

	}

	private static void calculateInDegree(Map<Integer, Integer> map) {

		for (int i = 0; i < GRAPH.length; i++) {
			for (int j = 0; j < GRAPH.length; j++) {
				Integer inDegree = map.get(j);
				if (GRAPH[i][j] == DIRECT_FLAG) {
					inDegree++;
					map.put(j, inDegree);
				}
			}

		}
	}

	private static Stack<Integer> initStack(Map<Integer, Integer> map) throws IllegalAccessException {
		Stack<Integer> stack = new Stack<>();
		Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
		Iterator<Map.Entry<Integer, Integer>> it = entries.iterator();
		while (it.hasNext()) {
			Map.Entry<Integer, Integer> next = it.next();
			Integer inDegree = next.getValue();
			if (0 == inDegree) {
				stack.push(next.getKey());
			}
		}
		if (stack.isEmpty()) {
			throw new IllegalAccessException("构成环,无法拓扑排序");
		}
		return stack;
	}

	private static void topologicalSort(Map<Integer, Integer> map, Stack<Integer> stack) throws IllegalAccessException {
		List<Integer> sortedList = new ArrayList<>(14);
		while (!stack.isEmpty()) {
			Integer endPoint = stack.pop();
			sortedList.add(endPoint);

			for (int i = 0; i < GRAPH.length; i++) {
				int value = GRAPH[endPoint][i];
				if (value == DIRECT_FLAG) {
					Integer inDegree = map.get(i);
					inDegree--;
					map.put(i, inDegree);
					if (inDegree == 0) {
						stack.push(i);
					}
				}
			}
		}
		if (sortedList.size() != GRAPH.length) {
			throw new IllegalAccessException("构成环,无法拓扑排序");
		}
		System.out.print("拓扑排序结果:");
		for (Integer temp : sortedList) {
			System.out.print("V" + temp + "->");
		}


	}
}

总结

以上就是今天要讲的内容,本文仅仅简单介绍了一下拓扑排序原理及其一种实现排序的方式。本文所展示的排序结果并不是唯一结果,满足拓扑序列的可以有很多个。例如V0也可以排在最前面,因为它也是没有入度,除非那种极端情况一条线的,一般来说满足拓扑序列的结果都是有多个的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值