1 定义

在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,称为AOV网(Activity On Vertex Network)

设G={V,E}是一个具有n个顶点的有向图,V中的顶点序列v1、v2… 满足若从顶点vi到vj有一条路径,则在顶点序列中顶点vi必须在顶点vj之前,则称这样的顶点序列为一个拓扑序列

拓扑排序就是对一个有向图构造拓扑序列的过程,构造时会有两个结果,如果此网的全部顶点都被输出,则说明它是不存在环(回路)的AOV网;如果输出顶点数少了,说明这个网存在环(回路),不是AOV网。一个不存在回路的AOV网,可以应用在各种各样的工程或项目的流程图中,满足各种应用场景的需要。

对AOV网进行拓扑排序的基本思路是:

  1. 从AOV网中选择一个入度为0的顶点输出
  2. 然后删除此顶点及所有以此顶点为尾的弧
  3. 继续重复以上2个步骤,直到输出全部顶点或者AOV网中不存在入度为0的顶点为止

2 建立测试图

jquery拓扑图控件 js 拓扑排序_jquery拓扑图控件


由于拓扑排序的过程需要删除节点,因此选择邻接表存储图更合适,且我们可以给邻接表中每个顶点增添一个入度域,方便查找入度为0的顶点。

jquery拓扑图控件 js 拓扑排序_拓扑排序_02

class vex{
	constructor(value){
		this.data = value;
		this.firstEdge = null;
		this.in = 0;   //用于存放顶点的入度
	}

}

class adjvex{
	constructor(node,weight){
		this.node = node;
		this.weight = weight;
		this.next = null;
	}
}

class Graph{
	constructor(v,vr){
		let len = v.length;
		let vexs = new Array(len);
		let v1=0,v2=0;
		let newvex = null;
		for (let i=0;i<len;i++){
			vexs[i] = new vex(v[i]);
		}
		for (let arc of vr){
			v1 = v.indexOf(arc[0]);
			v2 = v.indexOf(arc[1]);

			newvex = new adjvex(v2,arc[2]);
			newvex.next = vexs[v1].firstEdge;
			vexs[v1].firstEdge = newvex;
			vexs[v2].in++;
		}
		this.adjList = vexs;
	}
}

let a = new Graph(['v0','v1','v2','v3','v4','v5','v6','v7','v8','v9','v10','v11','v12','v13'],[['v0','v11',1],['v0','v4',1],['v0','v5',1],['v1','v4',1],['v1','v8',1],['v1','v2',1],['v2','v5',1],['v2','v6',1],['v3','v2',1],['v3','v13',1],['v4','v7',1],['v5','v8',1],['v5','v12',1],['v6','v5',1],['v8','v7',1],['v9','v11',1],['v9','v10',1],['v10','v13',1],['v12','v9',1]]);
console.log(a);

jquery拓扑图控件 js 拓扑排序_v9_03

3 拓扑排序算法

function topoSort(G){
	let stack = [];    //辅助栈
	for (let i=0;i<G.adjList.length;i++){   //寻找入度为0的顶点推入栈
		if (G.adjList[i].in === 0){
			stack.push(i);
		}
	}

	let currentVex = null;
	let count = 0;         //用于计数已经输出的顶点
	while(stack.length > 0){
		currentVex = G.adjList[stack.pop()];
		console.log(currentVex.data);      //输出栈顶顶点
		count++;
		currentVex = currentVex.firstEdge;
		while(currentVex){        //删除当前顶点,遍历其邻接顶点,使它们入度减1
			if ((--G.adjList[currentVex.node].in) === 0){  //当邻接顶点入度为0时
				stack.push(currentVex.node);    //将邻接顶点压入栈中
			}
			currentVex = currentVex.next;
		}
	}

	if (count < G.adjList.length){   //若输出的顶点数少于图中顶点数,则存在环
		console.log("存在环路");
		return false;
	}else{
		return true;
	}
}

jquery拓扑图控件 js 拓扑排序_jquery拓扑图控件_04


第一次循环将入度为0的顶点入栈时间复杂度为O(n),在while循环中,扫描邻接顶点,时间复杂度为O(e),因此整体算法的时间复杂度为O(n+e)