图 :表示多对多的关系
包含: 一组顶点V 表示顶点的集合,一组边E 表示边的集合
(v,w)无向边 <v,w> 有向边
三要素: 1.图,2.G(V,E)是由一个非空的有限顶点集合V和一个有限边集合E组成 必须有一个顶点 3.操作集
常见术语:所有边无方向的 是无向图 有向的 有向图
怎么在程序中表示一个图?
1.邻接矩阵G[N][N]-------N个顶点从0到N-1编号 G[i][[j]={1(v),0(e)}
特点 1.对角线 为0 2. 这个图是对称的
对于无向图的存储,怎样可以节省一般的空间?
用一个长度为N(N+1)/2的1维数组A存储(G00,G10,G11,Gn-1n-1),改变后它的下标是(i*(i+1)/2+j)
对于网络的将G[i][j]的值定义为边<vi,vj>的权重即可
行 表示出度 列 表示入度
邻接表 一定要足够稀疏
深度优先搜索DFS
DFS 对应 先序遍历
BFS 对应 层序遍历
public interface Graph {
public static final int UndirectedGraph=0; // 无向图
public static final int DirectedGraph=1; // 有向图
//返回图的类型1
public int getType();
//返回图的顶点数2
public int getVexNum();
//返回图的边数3
public int getEdgeNum();
//返回图的所有顶点4
public Iterator getVertex();
//返回图的所有边5
public Iterator getEdge();
//删除一个顶点6
public void remove(Vertex vertex);
//删除一条边7
public void remove(Edge edge);
//添加一个顶点8
public Node insert(Vertex vertex);
//添加一条边9
public Node insert(Edge edge);
// 判断顶点u,v是否邻接,是否有边从u到v10
public boolean areAdjacent(Vertex u,Vertex v);
//返回从u指向v的边,不存在即返回null 11
public Edge edgeFromTo(Vertex u,Vertex v);
//返回从u出发可以直接到达的邻接顶点 12
public Iterator adjVertexs(Vertex u);
//对图进行深度优先遍历 13
public Iterator DFSTraverse(Vertex v);
//对图进行广度优先遍历 14
public Iterator BFSTraverse(Vertex v);
//求顶点V到其他顶点的最短路径15
public Iterator shortestPath(Vertex v);
//求无向图的最小生成树,如果是有向图无此操作
public void generateMST() throws UnsupportedOperation;
//求有向图的拓扑排序
public Iterator toplogicalSort() throws UnsupportedOperation;
//求有向无环图的关键路径
public void criticalPath() throws UnsupportedOperation;
}
UnsupportOperation
public class UnsupportedOperation extends RuntimeException{
public UnsupportedOperation(String str){
super(str);
}
}
// 双链式存储结构的顶点定义
public class Vertex {
private Object info; //顶点信息
private LinkedList adjacentEdges; //顶点的邻接边表
private LinkedList reAdjacentEdges; //顶点的逆邻接边表,无向图时为空
private boolean visited; //访问状态
private Node vexPosition; //顶点在顶点表中的位置
private int graphType; //顶点所在图的类型
private Object application; //应用。如求最短路径时为 Path,求关键路径时为 Vtime
//构造方法:在图 G 中引入一个新顶点
public Vertex(Graph g, Object info) {
this.info = info;
adjacentEdges = new LinkedListDLNode();
reAdjacentEdges = new LinkedListDLNode();
visited = false;
graphType = g.getType();
vexPosition = g.insert(this);
application = null;
}
//辅助方法:判断顶点所在图的类型
private boolean isUnDiGraphNode(){ return graphType==Graph.UndirectedGraph;}
//获取或设置顶点信息
public Object getInfo(){ return info;}
public void setInfo(Object obj){ this.info = info;}
//与顶点的度相关的方法
public int getDeg(){
if (isUnDiGraphNode())
return adjacentEdges.getSize(); //无向图顶点的(出/入)度为邻接边表规模
else
return getOutDeg()+getInDeg(); //有向图顶点的度为出度与入度之和
}
public int getOutDeg(){
return adjacentEdges.getSize(); //有(无)向图顶点的出度为邻接表规模
}
public int getInDeg(){
if (isUnDiGraphNode())
return adjacentEdges.getSize(); //无向图顶点的入度就是它的度
else
return reAdjacentEdges.getSize(); //有向图顶点入度为逆邻接表的规模
}
//获取与顶点关联的边
public LinkedList getAdjacentEdges(){ return adjacentEdges;}
public LinkedList getReAdjacentEdges(){
if (isUnDiGraphNode())
return adjacentEdges; //无向图顶点的逆邻接边表就是其邻接边表
else
return reAdjacentEdges;
}
//取顶点在所属图顶点集中的位置
public Node getVexPosition(){ return vexPosition;}
//与顶点访问状态相关方法
public boolean isVisited(){ return visited;}
public void setToVisited(){ visited = true;}
public void setToUnvisited(){ visited = false;}
//取或设置顶点应用信息
protected Object getAppObj(){ return application;}
protected void setAppObj(Object app){ application = app;}
//重置顶点状态信息
public void resetStatus(){
visited = false;
application = null;
}
}
public class Edge {
public static final int NORMAL = 0;
public static final int MST = 1; //MST 边
public static final int CRITICAL = 2;//关键路径中的边
private int weight; //权值
private Object info; //边的信息
private Node edgePosition; //边在边表中的位置
private Node firstVexPosition; //边的第一顶点与第二顶点
private Node secondVexPosition; //在顶点表中的位置
private Node edgeFirstPosition; //边在第一(二)顶点的邻接(逆邻接)边表中的位置
private Node egdeSecondPosition;//在无向图中就是在两个顶点的邻接边表中的位置
private int type; //边的类型
private int graphType; //所在图的类型
//构造方法:在图 G 中引入一条新边,其顶点为 u、 v
public Edge(Graph g, Vertex u, Vertex v, Object info){
this(g,u,v,info,1);
}
public Edge(Graph g, Vertex u, Vertex v, Object info, int weight) {
this.info = info;
this.weight = weight;
edgePosition = g.insert(this);
firstVexPosition = u.getVexPosition();
secondVexPosition = v.getVexPosition();
type = Edge.NORMAL;
graphType = g.getType();
if (graphType==Graph.UndirectedGraph){
//如果是无向图,边应当加入其两个顶点的邻接边表
edgeFirstPosition = u.getAdjacentEdges().insertLast(this);
egdeSecondPosition = v.getAdjacentEdges().insertLast(this);
}else {
//如果是有向图,边加入起始点的邻接边表,终止点的逆邻接边表
edgeFirstPosition = u.getAdjacentEdges().insertLast(this);
egdeSecondPosition = v.getReAdjacentEdges().insertLast(this);
}
}
//get&set methods
public Object getInfo(){ return info;}
public void setInfo(Object obj){ this.info = info;}
public int getWeight(){ return weight;}
public void setWeight(int weight){ this.weight = weight;}
public Vertex getFirstVex(){ return (Vertex)firstVexPosition.getData();}
public Vertex getSecondVex(){ return (Vertex)secondVexPosition.getData();}
public Node getFirstVexPosition(){ return firstVexPosition;}
public Node getSecondVexPosition(){ return secondVexPosition;}
public Node getEdgeFirstPosition(){ return edgeFirstPosition;}
public Node getEdgeSecondPosition(){ return egdeSecondPosition;}
public Node getEdgePosition(){ return edgePosition;}
//与边的类型相关的方法
public void setToMST(){ type = Edge.MST;}
public void setToCritical(){ type = Edge.CRITICAL;}
public void resetType(){ type = Edge.NORMAL;}
public boolean isMSTEdge(){ return type==Edge.MST;}
public boolean isCritical(){ return type==Edge.CRITICAL;}
}
深度优先搜索(depth first search) 遍历类似于树的先根遍历,是树的先根遍历的推广。
深度优先搜索的基本方法是:从图中某个顶点发 v 出发,访问此顶点,然后依次从 v
的未被访问的邻接点出发深度优先遍历图,直至图中所有和 v 有路径相通的顶点都被访问
到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述
过程,直至图中所有顶点都被访问到为止。
以图 7-13(a)中无向图为例,对其进行深度优先搜索遍历的过程如图 7-13(c)所示,
其中黑色的实心箭头代表访问方向,空心箭头代表回溯方向,箭头旁的数字代表搜索顺序,
顶点 a 是起点。遍历过程如下:首先访问顶点 a,然后
a) 顶点 a 的未曾访问的邻接点有 b、 d、 e,选择邻接点 b 进行访问;
b) 顶点 b 的未曾访问的邻接点有 c、 e,选择邻接点 c 进行访问;
c) 顶点 c 的未曾访问的邻接点有 e、 f,选择邻接点 e 进行访问;
d) 顶点 e 的未曾访问的邻接点只有 f,访问 f;
e) 顶点 f 无未曾访问的邻接点,回溯至 e;
f) 顶点 e 无未曾访问的邻接点,回溯至 c;
g) 顶点 c 无未曾访问的邻接点,回溯至 b;
h) 顶点 b 无未曾访问的邻接点,回溯至 a;
i) 顶点 a 还有未曾访问的邻接点 d,访问 d;
j) 顶点 d 无未曾访问的邻接点,回溯至 a。
到此, a 再没有未曾访问的邻接点,也不能向前回溯,从 a 出发能够访问的顶点均已访问,并且此时图中再没有未曾访问的顶点,因此遍历结束。由以上过程得到的遍历序列为: a , b ,
c , e , f , d。
对于有向图而言,深度优先搜索的执行过程一样,例如图 7-13(b)中有向图的深度优
先搜索过程如图 7-13(d)所示。在这里需要注意的是从顶点 a 出发深度优先搜索只能访问
到 a , b , c , e , f,而无法访问到图中所有顶点,所以搜索需要从图中另一个未曾访问的顶点
d 开始进行新的搜索,即图 7-13(d)中的第 9 步
public Iterator DFSTraverse(Vertex v) {
LinkedList traverseSeq = new LinkedListDLNode();//遍历结果
resetVexStatus(); //重置顶点状态
DFSRecursion(v, traverseSeq); //从 v 点出发深度优先搜索
Iterator it = getVertex(); //从图未曾访问的其他顶点重新搜索(调用图操作③)
for (it.first(); !it.isDone(); it.next()) {
Vertex u = (Vertex) it.currentItem();
if (!u.isVisited()) DFSRecursion(u, traverseSeq);
}
return traverseSeq.elements();
}
//从顶点 v 出发深度优先搜索的递归算法
private void DFSRecursion(Vertex v, LinkedList list) {
v.setToVisited(); //设置顶点 v 为已访问
list.insertLast(v); //访问顶点 v
Iterator it = adjVertexs(v); //取得顶点 v 的所有邻接点(调用图操作⑧)
for (it.first(); !it.isDone(); it.next()) {
Vertex u = (Vertex) it.currentItem();
if (!u.isVisited()) DFSRecursion(u, list);
}
}
//从顶点 v 出发深度优先搜索的非递归算法
private void DFS(Vertex v, LinkedList list) {
Stack s = new StackSLinked();
s.push(v);
while (!s.isEmpty()) {
Vertex u = (Vertex) s.pop(); //取栈顶元素
if (!u.isVisited()) { //如果没有访问过
u.setToVisited(); //访问之
list.insertLast(u);
Iterator it = adjVertexs(u); //未访问的邻接点入栈(调用图操作⑧)
for (it.first(); !it.isDone(); it.next()) {
Vertex adj = (Vertex) it.currentItem();
if (!adj.isVisited()) s.push(adj);
}//for
}//if
}//while
}
public Iterator BFSTraverse(Vertex v) {
LinkedList traverseSeq = new LinkedListDLNode();//遍历结果
resetVexStatus(); //重置顶点状态
BFS(v, traverseSeq); //从 v 点出发广度优先搜索
Iterator it = getVertex(); //从图中未访问的顶点重新搜索(调用图操作③)
for (it.first(); !it.isDone(); it.next()) {
Vertex u = (Vertex) it.currentItem();
if (!u.isVisited()) BFS(u, traverseSeq);
}
return traverseSeq.elements();
}
private void BFS(Vertex v, LinkedList list) {
Queue q = new QueueSLinked();
v.setToVisited(); //访问顶点 v
list.insertLast(v);
q.enqueue(v); //顶点 v 入队
while (!q.isEmpty()) {
Vertex u = (Vertex) q.dequeue(); //队首元素出队
Iterator it = adjVertexs(u); //访问其未曾访问的邻接点,并入队
for (it.first(); !it.isDone(); it.next()) {
Vertex adj = (Vertex) it.currentItem();
if (!adj.isVisited()) {
adj.setToVisited();
list.insertLast(adj);
q.enqueue(adj);
}//if
}//for
}//while
}
首先,以顶点的成员变量 visited 来表示该
顶点是否属于 S, visited = true 表示属于 S,否则不属于 S。其次,到达 V-S 中各个顶点的最
短横切边通过该顶点的成员变量 application 来表示,此时 application 指向的是 Edge 类的对
象,它是从 S 到达本顶点横切边中权值最小的一条。在构造最小生成树过程中,对顶点成
员变量 application 的操作方法见代码 7-5。最后,最小生成树的表示采用设置图中边的类型
来完成,即如果是最小生成树的边,将边的类型设置为 Edge.MST。
//获取到达顶点 v 的最小横切边权值
protected int getCrossWeight(Vertex v) {
if (getCrossEdge(v) != null)
return getCrossEdge(v).getWeight();
else return Integer.MAX_VALUE;
}
//获取到达顶点 v 的最小横切边
protected Edge getCrossEdge(Vertex v) {
return (Edge) v.getAppObj();
}
//设置到达顶点 v 的最小横切边
protected void setCrossEdge(Vertex v, Edge e) {
v.setAppObj(e);
}
public void generateMST() {
resetVexStatus(); //重置图中各顶点的状态信息
resetEdgeType(); //重置图中各边的类型信息
Iterator it = getVertex(); //(调用图操作③)
Vertex v = (Vertex) it.currentItem();//选第一个顶点作为起点
v.setToVisited(); //顶点 v 进入集合 S
//初始化顶点集合 S 到 V-S 各顶点的最短横切边
for (it.first(); !it.isDone(); it.next()) {
Vertex u = (Vertex) it.currentItem();
Edge e = edgeFromTo(v, u); //(调用图操作⑦)
setCrossEdge(u, e); //设置到达 V-S 中顶点 u 的最短横切边
}
for (int t = 1; t < getVexNum(); t++) { //进行|V|-1 次循环找到|V|-1 条边
Vertex k = selectMinVertex(it); //选择轻边在 V-S 中的顶点 k
k.setToVisited(); //顶点 k 加入 S
Edge mst = getCrossEdge(k); //割(S , V - S) 的轻边
if (mst != null) mst.setToMST(); //将边加入 MST
//以 k 为中间顶点修改 S 到 V-S 中顶点的最短横切边
Iterator adjIt = adjVertexs(k); //取出 k 的所有邻接点
for (adjIt.first(); !adjIt.isDone(); adjIt.next()) {
Vertex adjV = (Vertex) adjIt.currentItem();
Edge e = edgeFromTo(k, adjV); //(调用图操作⑦)
if (e.getWeight() < getCrossWeight(adjV))//发现到达 adjV 更短的横切边
setCrossEdge(adjV, e);
}//for
}//for(int t=1...
}
//查找轻边在 V-S 中的顶点
protected Vertex selectMinVertex(Iterator it) {
Vertex min = null;
for (it.first(); !it.isDone(); it.next()) {
Vertex v = (Vertex) it.currentItem();
if (!v.isVisited()) {
min = v;
break;
}
}
for (; !it.isDone(); it.next()) {
Vertex v = (Vertex) it.currentItem();
if (!v.isVisited() && getCrossWeight(v) < getCrossWeight(min))
min = v;
}
return min;
}
首先,以顶点 v 的成员变量 visited 来表示
该顶点是否属于 S, visited = true 表示属于 S,否则属于 V-S。其次,从 s 到达 V-S 中各个顶
点 v 的最短路径通过 v 的成员变量 application 来表示,此时 application 指向的是 Path 类的
对象。该对象是从 s 到达 v 的当前最短路径,其中包含 v 的当前最短距离 distance,以及取
得最短距离的路径上途经的顶点。在 Dijkstra 算法执行过程中,对顶点成员变量 application
的操作方法见代码 7-6。最后,求得从 s 到其余顶点的所有最短路径通过迭代器对象返回
//取或设置顶点 v 的当前最短距离
protected int getDistance(Vertex v) {
return ((Path) v.getAppObj()).getDistance();
}
protected void setDistance(Vertex v, int dis) {
((Path) v.getAppObj()).setDistance(dis);
}
//取或设置顶点 v 的当前最短路径
protected Path getPath(Vertex v) {
return (Path) v.getAppObj();
}
protected void setPath(Vertex v, Path p) {
v.setAppObj(p);
}
public class Path {
private int distance; //起点与终点的距离
private Vertex start; //起点信息
private Vertex end; //终点信息
private LinkedList pathInfo; //起点到终点途经的顶点序列
//构造方法
public Path() {
this(Integer.MAX_VALUE, null, null);
}
public Path(int distance, Vertex start, Vertex end) {
this.distance = distance;
this.start = start;
this.end = end;
pathInfo = new LinkedListDLNode();
}
//判断起点与终点之间是否存在路径
public boolean hasPath() {
return distance != Integer.MAX_VALUE && start != null && end != null;
}
//求路径长度
public int pathLength() {
if (!hasPath()) return -1;
else if (start == end) return 0;
else return pathInfo.getSize() + 1;
}
//get&set methods
public void setDistance(int dis) {
distance = dis;
}
public void setStart(Vertex v) {
start = v;
}
public void setEnd(Vertex v) {
end = v;
}
public int getDistance() {
return distance;
}
public Vertex getStart() {
return start;
}
public Vertex getEnd() {
return end;
}
public Iterator getPathInfo() {
return pathInfo.elements();
}
//清空路经信息
public void clearPathInfo() {
pathInfo = new LinkedListDLNode();
}
//添加路径信息
public void addPathInfo(Object info) {
pathInfo.insertLast(info);
}
}
public Iterator shortestPath(Vertex v) {
LinkedList sPath = new LinkedListDLNode(); //所有的最短路径序列
resetVexStatus(); //重置图中各顶点的状态信息
//初始化,将 v 到各顶点的最短距离初始化为由 v 直接可达的距离
Iterator it = getVertex(); //(调用图操作③)
for (it.first(); !it.isDone(); it.next()) {
Vertex u = (Vertex) it.currentItem();
int weight = Integer.MAX_VALUE;
Edge e = edgeFromTo(v, u); //(调用图操作⑦)
if (e != null) weight = e.getWeight();
if (u == v) weight = 0;
Path p = new Path(weight, v, u);
setPath(u, p);
}
v.setToVisited(); //顶点 v 进入集合 S
sPath.insertLast(getPath(v)); //求得的最短路径进入链接表
for (int t = 1; t < getVexNum(); t++) { //进行|V|-1 次循环找到|V|-1 条最短路径
Vertex k = selectMin(it); //找 V-S 中 distance 最小的点 k
k.setToVisited(); //顶点 k 加入 S
sPath.insertLast(getPath(k)); //求得的最短路径进入链接表
int distK = getDistance(k); //修正 V-S 中顶点当前最短路径
Iterator adjIt = adjVertexs(k); //取出 k 的所有邻接点
for (adjIt.first(); !adjIt.isDone(); adjIt.next()) {
Vertex adjV = (Vertex) adjIt.currentItem(); //k 的邻接点 adjV
Edge e = edgeFromTo(k, adjV); //(调用图操作⑦)
//发现更短的路径
if ((long) distK + (long) e.getWeight() < (long) getDistance(adjV)) {
setDistance(adjV, distK + e.getWeight());
amendPathInfo(k, adjV); //以 k 的路径信息修改 adjV 的路径信息
}
}//for
}//for(int t=1...
return sPath.elements();
}
//在顶点集合中选择路径距离最小的
protected Vertex selectMin(Iterator it) {
Vertex min = null;
for (it.first(); !it.isDone(); it.next()) {
Vertex v = (Vertex) it.currentItem();
if (!v.isVisited()) {
min = v;
break;
}
}
for (; !it.isDone(); it.next()) {
Vertex v = (Vertex) it.currentItem();
if (!v.isVisited() && getDistance(v) < getDistance(min))
min = v;
}
return min;
}
为得到 AOV 网络的拓扑序列,可以使用以下方法:
⑴ 在 AOV 网络中选一个没有直接前驱的顶点,并输出之;
⑵ 从图中删去该顶点,同时删去所有它发出的有向边;
⑶ 重复以上⑴、⑵步, 直到全部顶点均已输出,或图中不存在无前驱的顶点。
//取或设置顶点 v 的当前入度
private int getTopInDe(Vertex v) {
return ((Integer) v.getAppObj()).intValue();
}
private void setTopInDe(Vertex v, int indegree) {
v.setAppObj(Integer.valueOf(indegree));
}
⑴ 建立入度为零的顶点栈;
⑵ 当入度为零的顶点栈不空时,重复执行
从顶点栈中退出一个顶点,并输出之;
搜索以这个顶点发出的边,将边的终顶点入度减 1;
如果边的终顶点入度减至 0,则该顶点进入栈;
⑶ 如果输出顶点个数少于 AOV 网络的顶点个数,说明网络中存在有向环。
在具体的算法实现中,我们使用每个顶点的 application 成员变量指向一个 Integer 对象,
它表示顶点在算法执行中当前的入度。在拓扑排序过程中对顶点成员变量 application 的操作
//取或设置顶点 v 的当前入度
private int getTopInDe(Vertex v) {
return ((Integer) v.getAppObj()).intValue();
}
private void setTopInDe(Vertex v, int indegree) {
v.setAppObj(Integer.valueOf(indegree));
}
public Iterator toplogicalSort() {
LinkedList topSeq = new LinkedListDLNode(); //拓扑序列
Stack s = new StackSLinked();
Iterator it = getVertex();
for (it.first(); !it.isDone(); it.next()) { //初始化顶点集应用信息
Vertex v = (Vertex) it.currentItem();
v.setAppObj(Integer.valueOf(v.getInDeg()));
if (v.getInDeg() == 0) s.push(v);
}
while (!s.isEmpty()) {
Vertex v = (Vertex) s.pop();
topSeq.insertLast(v); //生成拓扑序列
Iterator adjIt = adjVertexs(v); //对于 v 的每个邻接点入度减 1
for (adjIt.first(); !adjIt.isDone(); adjIt.next()) {
Vertex adjV = (Vertex) adjIt.currentItem();
int in = getTopInDe(adjV) - 1;
setTopInDe(adjV, in);
if (in == 0) s.push(adjV); //入度为 0 的顶点入栈
}//for adjIt
}//while
if (topSeq.getSize() < getVexNum()) return null;
else return topSeq.elements();
}
//求关键路径算法中,对 v.application 的操作
//取顶点 v 的最早开始时间与最迟开始时间
private int getVE(Vertex v) {
return ((Vtime) v.getAppObj()).getVE();
}
private int getVL(Vertex v) {
return ((Vtime) v.getAppObj()).getVL();
}
//设置顶点 v 的最早开始时间与最迟开始时间
private void setVE(Vertex v, int ve) {
((Vtime) v.getAppObj()).setVE(ve);
}
private void setVL(Vertex v, int vl) {
((Vtime) v.getAppObj()).setVL(vl);
}
public class Vtime {
private int ve; //最早发生时间
private int vl; //最迟发生时间
//构造方法
public Vtime() {
this(0, Integer.MAX_VALUE);
}
public Vtime(int ve, int vl) {
this.ve = ve;
this.vl = vl;
}
//get&set method
public int getVE() {
return ve;
}
public int getVL() {
return vl;
}
public void setVE(int t) {
ve = t;
}
public void setVL(int t) {
vl = t;
}
}
public void criticalPath() {
Iterator it = toplogicalSort();
resetEdgeType(); //重置图中各边的类型信息
if (it == null) return;
LinkedList reTopSeq = new LinkedListDLNode(); //逆拓扑序列
for (it.first(); !it.isDone(); it.next()) { //初始化各点 ve 与 vl,并生成逆拓扑序列
Vertex v = (Vertex) it.currentItem();
Vtime time = new Vtime(0, Integer.MAX_VALUE); //ve=0,vl=∞
v.setAppObj(time);
reTopSeq.insertFirst(v);
}
for (it.first(); !it.isDone(); it.next()) { //正向拓扑序列求各点 ve
Vertex v = (Vertex) it.currentItem();
Iterator adjIt = adjVertexs(v);
for (adjIt.first(); !adjIt.isDone(); adjIt.next()) {
Vertex adjV = (Vertex) adjIt.currentItem();
Edge e = edgeFromTo(v, adjV);
if (getVE(v) + e.getWeight() > getVE(adjV)) //更新最早开始时间
setVE(adjV, getVE(v) + e.getWeight());
}
}
Vertex dest = (Vertex) reTopSeq.first().getData();
setVL(dest, getVE(dest)); //设置汇点 vl=ve
Iterator reIt = reTopSeq.elements();
for (reIt.first(); !reIt.isDone(); reIt.next()) { //逆向拓扑序列求各点 vl
Vertex v = (Vertex) reIt.currentItem();
Iterator adjIt = adjVertexs(v);
for (adjIt.first(); !adjIt.isDone(); adjIt.next()) {
Vertex adjV = (Vertex) adjIt.currentItem();
Edge e = edgeFromTo(v, adjV);
if (getVL(v) > getVL(adjV) - e.getWeight()) //更新最迟开始时间
setVL(v, getVL(adjV) - e.getWeight());
}
}
Iterator edIt = edges.elements();
for (edIt.first(); !edIt.isDone(); edIt.next()) { //求关键活动
Edge e = (Edge) edIt.currentItem();
Vertex u = e.getFirstVex();
Vertex v = e.getSecondVex();
if (getVE(u) == getVL(v) - e.getWeight()) e.setToCritical();
}
}