一、什么是拓扑排序?
对一个有向图,设G=(V,E),V表示的是顶点集合,E表示的是顶点间的边关系,若 Vi -> Vj存在路径,则Vi一定排在Vj之前,则我们称这样的顶点序列为拓扑序列,使之构成拓扑序列的过程叫做拓扑排序。
由上述可知,拓扑序列有几点特点:
- 有向图
- 无环
二、实现思想
由第一章节可知,根据拓扑排序思想,排在前列的都是没有指向的,所以我们首先要找入度为0的顶点,因为入度为0的顶点,不存在指向它的顶点,也就意味着没有排在它前面的顶点。
三、实现步骤
- 初始化图
- 初始化所有顶点的入度0
- 统计所有顶点的入度
- 找到入度为0的顶点入栈
- 栈顶元素出栈,找到栈顶元素指向其他顶点的边,并减少该顶点入度1,若此时如果该顶点入度为0的话就入栈。
- 循环步骤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也可以排在最前面,因为它也是没有入度,除非那种极端情况一条线的,一般来说满足拓扑序列的结果都是有多个的。