相關(guān)概念
AOE網(wǎng):在一個(gè)表示工程的帶權(quán)有向圖中,用頂點(diǎn)表示事件,用有向邊表示活動(dòng),用邊上的權(quán)值表示活動(dòng)的持續(xù)時(shí)間,這種有向圖的邊表示活動(dòng)的網(wǎng),我們稱之為AOE網(wǎng)(Activity On Edge Network)。
關(guān)鍵路徑與拓?fù)渑判虻膮^(qū)別:拓?fù)渑判蛑饕墙鉀Q一個(gè)工程能否順利進(jìn)行的問題,關(guān)鍵路徑解決的是工程完成所需要的最短時(shí)間問題。換句話說,關(guān)鍵路徑問題需要在拓?fù)渑判虻幕A(chǔ)上得到完成工程的最短時(shí)間,因?yàn)楣こ淌紫缺仨毷强梢员煌瓿傻模簿褪蔷W(wǎng)中不存在環(huán)路時(shí),才能進(jìn)一步討論工程完成所需的最短時(shí)間問題。
AOE網(wǎng)和AOV網(wǎng):AOV網(wǎng)是頂點(diǎn)表示活動(dòng)的網(wǎng),它只描述活動(dòng)之間的制約關(guān)系;AOE網(wǎng)是用邊表示活動(dòng)的網(wǎng),邊上的權(quán)值表示活動(dòng)持續(xù)的時(shí)間。
關(guān)鍵路徑:從源點(diǎn)到匯點(diǎn)具有最大長度的路徑稱為關(guān)鍵路徑,在關(guān)鍵路徑上的活動(dòng)叫關(guān)鍵活動(dòng)。只有縮短關(guān)鍵路徑上的關(guān)鍵活動(dòng)時(shí)間才可以減少整個(gè)工期長度。
關(guān)鍵路徑算法參數(shù)介紹
事件的最早發(fā)生時(shí)間etv(earliest time of vertex):頂點(diǎn)Vn的最早發(fā)生時(shí)間;
事件的最晚發(fā)生時(shí)間ltv(latest tiem of vertex):頂點(diǎn)Vn的最晚發(fā)生時(shí)間;
活動(dòng)的最早開工時(shí)間ete(earliest time of edge):弧En的最早發(fā)生時(shí)間;
活動(dòng)的最晚開工時(shí)間lte(latest time of edge):弧En的最晚發(fā)生時(shí)間,也就是不推遲工期的最晚開工時(shí)間。
關(guān)鍵路徑算法由etv和ltv求得ete和lte,當(dāng)ete與lte相等時(shí)表示活動(dòng)沒有空閑時(shí)間,是關(guān)鍵活動(dòng)。
etv和ltv數(shù)組的計(jì)算方法
P[k]表示所有到達(dá)頂點(diǎn)Vk的弧的集合,即Vk是弧的終點(diǎn)。
S[k]表示所有從頂點(diǎn)Vk出發(fā)的弧的集合,即Vk是弧的起點(diǎn)。
源代碼
帶權(quán)有向圖采用鄰接表的數(shù)據(jù)結(jié)構(gòu)表示
頂點(diǎn)表結(jié)點(diǎn)類
/**
* 頂點(diǎn)表結(jié)點(diǎn)類
*
* @author Shaw
*
*/
class VertexNode {
// 頂點(diǎn)入度
private int in;
// 頂點(diǎn)域
private String data;
// 指向邊表
private EdgeNode firstEdge;
public VertexNode(int in, String data, EdgeNode firstEdge) {
super();
this.in = in;
this.data = data;
this.firstEdge = firstEdge;
}
public int getIn() {
return in;
}
public void setIn(int in) {
this.in = in;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public EdgeNode getFirstEdge() {
return firstEdge;
}
public void setFirstEdge(EdgeNode firstEdge) {
this.firstEdge = firstEdge;
}
}
邊表頂點(diǎn)類
/**
* 邊表頂點(diǎn)類
* @author Shaw
*
*/
class EdgeNode {
//鄰接點(diǎn)域,存儲(chǔ)該頂點(diǎn)對應(yīng)的下標(biāo)
private int adjvex;
//權(quán)值域
private int weight;
//指向下一個(gè)鄰接點(diǎn)
private EdgeNode next;
public EdgeNode(int adjvex, int weight, EdgeNode next) {
super();
this.adjvex = adjvex;
this.weight = weight;
this.next = next;
}
public int getAdjvex() {
return adjvex;
}
public void setAdjvex(int adjvex) {
this.adjvex = adjvex;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public EdgeNode getNext() {
return next;
}
public void setNext(EdgeNode next) {
this.next = next;
}
}
關(guān)鍵路徑算法核心類
/**
* 關(guān)鍵路徑算法
*
* @author Shaw
*
*/
class CriticalPath {
private List<VertexNode> verList;
private int[] etv; // (earliest time of edge)事件最早發(fā)生時(shí)間數(shù)組
private int[] ltv; // (latest time of edge)事件最遲發(fā)生時(shí)間數(shù)組
private Stack<Integer> stack2; // 用于存放拓?fù)渑判蚪Y(jié)果的棧
// 建圖
public void buildAoeGraph() {
VertexNode v0 = new VertexNode(0, "v0", null);
EdgeNode v0e1 = new EdgeNode(2, 4, null);
EdgeNode v0e2 = new EdgeNode(1, 3, null);
v0.setFirstEdge(v0e1);
v0e1.setNext(v0e2);
VertexNode v1 = new VertexNode(0, "v1", null);
EdgeNode v1e1 = new EdgeNode(4, 6, null);
EdgeNode v1e2 = new EdgeNode(3, 5, null);
v1.setFirstEdge(v1e1);
v1e1.setNext(v1e2);
VertexNode v2 = new VertexNode(0, "v2", null);
EdgeNode v2e1 = new EdgeNode(5, 7, null);
EdgeNode v2e2 = new EdgeNode(3, 8, null);
v2.setFirstEdge(v2e1);
v2e1.setNext(v2e2);
VertexNode v3 = new VertexNode(0, "v3", null);
EdgeNode v3e1 = new EdgeNode(4, 3, null);
v3.setFirstEdge(v3e1);
VertexNode v4 = new VertexNode(0, "v4", null);
EdgeNode v4e1 = new EdgeNode(7, 4, null);
EdgeNode v4e2 = new EdgeNode(6, 9, null);
v4.setFirstEdge(v4e1);
v4e1.setNext(v4e2);
VertexNode v5 = new VertexNode(0, "v5", null);
EdgeNode v5e1 = new EdgeNode(7, 6, null);
v5.setFirstEdge(v5e1);
VertexNode v6 = new VertexNode(0, "v6", null);
EdgeNode v6e1 = new EdgeNode(9, 2, null);
v6.setFirstEdge(v6e1);
VertexNode v7 = new VertexNode(0, "v7", null);
EdgeNode v7e1 = new EdgeNode(8, 5, null);
v7.setFirstEdge(v7e1);
VertexNode v8 = new VertexNode(0, "v8", null);
EdgeNode v8e1 = new EdgeNode(9, 3, null);
v8.setFirstEdge(v8e1);
VertexNode v9 = new VertexNode(0, "v9", null);
verList = new ArrayList<>();
verList.add(v0);
verList.add(v1);
verList.add(v2);
verList.add(v3);
verList.add(v4);
verList.add(v5);
verList.add(v6);
verList.add(v7);
verList.add(v8);
verList.add(v9);
}
// 拓?fù)渑判颍糜陉P(guān)鍵路徑的計(jì)算
// 求出事件的最早發(fā)生時(shí)間數(shù)組etv[]
public void topologicalSortForCriticalPath() {
Stack<Integer> stack = new Stack<>();
etv = new int[verList.size()];
stack2 = new Stack<>();
int count = 0;
// 初始化所有頂點(diǎn)表結(jié)點(diǎn)的入度值
for (int i = 0; i < verList.size(); i++) {
// 初始化etv數(shù)組
etv[i] = 0;
EdgeNode edgeNode = verList.get(i).getFirstEdge();
while (edgeNode != null) {
// 邊集表中出現(xiàn)的下標(biāo)(adjvex)所對應(yīng)的頂點(diǎn)入度加1
VertexNode vertexNode = verList.get(edgeNode.getAdjvex());
vertexNode.setIn(vertexNode.getIn() + 1);
edgeNode = edgeNode.getNext();
}
}
// 將所有入度為0的頂點(diǎn)入棧
for (int i = 0; i < verList.size(); i++) {
if (verList.get(i).getIn() == 0) {
stack.push(i);
}
}
while (!stack.isEmpty()) {
// 從棧中彈出一個(gè)入度為0的頂點(diǎn)
int vertex = stack.pop();
// 將出棧的頂點(diǎn)壓入stack2棧中用于關(guān)鍵路徑算法
stack2.push(vertex);
count++;
// 獲取彈出的頂點(diǎn)指向的第一個(gè)邊結(jié)點(diǎn)
EdgeNode edgeNode = verList.get(vertex).getFirstEdge();
while (edgeNode != null) {
// 獲取邊表結(jié)點(diǎn)的Adjvex值,該值存儲(chǔ)對應(yīng)頂點(diǎn)表結(jié)點(diǎn)的下標(biāo)值
int adjvex = edgeNode.getAdjvex();
// 獲取邊表結(jié)點(diǎn)指向的頂點(diǎn)表結(jié)點(diǎn)
VertexNode vertexNode = verList.get(adjvex);
// 刪除邊結(jié)點(diǎn),也就是將對應(yīng)的頂點(diǎn)表結(jié)點(diǎn)的入度減1
vertexNode.setIn(vertexNode.getIn() - 1);
// 如果該頂點(diǎn)表結(jié)點(diǎn)入度為0時(shí)壓棧
if (vertexNode.getIn() == 0) {
stack.push(adjvex);
}
// 求各頂點(diǎn)事件的最早發(fā)生時(shí)間值
if ((etv[vertex] + edgeNode.getWeight()) > etv[adjvex]) {
etv[adjvex] = etv[vertex] + edgeNode.getWeight();
}
// 獲取下一個(gè)邊表結(jié)點(diǎn)的值
edgeNode = edgeNode.getNext();
}
}
}
// 關(guān)鍵路徑算法
public void CriticalPath() {
EdgeNode edgeNode;
int i, stacktop, k, j;
// 活動(dòng)最早發(fā)生時(shí)間和最遲發(fā)生時(shí)間
int ete, lte;
topologicalSortForCriticalPath();
System.out.println("etv數(shù)組:");
for (i = 0; i < verList.size(); i++) {
System.out.print(etv[i] + " ");
}
// 事件最晚發(fā)生時(shí)間
ltv = new int[verList.size()];
// 初始化ltv數(shù)組
for (i = 0; i < verList.size(); i++) {
ltv[i] = etv[verList.size() - 1];
}
// while循環(huán)計(jì)算具體的ltv數(shù)組的值
while (!stack2.isEmpty()) {
//從拓?fù)渑判虻慕Y(jié)果從后往前遍歷,因?yàn)橄仁菑臈?中彈出后在壓入棧2的,所以棧2的頂是拓?fù)渑判虻淖詈笠粋€(gè)結(jié)果
stacktop = stack2.pop();
edgeNode = verList.get(stacktop).getFirstEdge();
while (edgeNode != null) {
k = edgeNode.getAdjvex();
if (ltv[k] - edgeNode.getWeight() < ltv[stacktop]) {
ltv[stacktop] = ltv[k] - edgeNode.getWeight();
}
edgeNode = edgeNode.getNext();
}
}
System.out.println("\nltv數(shù)組:");
for (i = 0; i < verList.size(); i++) {
System.out.print(ltv[i] + " ");
}
System.out.println("\n關(guān)鍵路徑:");
for (j = 0; j < verList.size(); j++) {
edgeNode = verList.get(j).getFirstEdge();
while (edgeNode != null) {
k = edgeNode.getAdjvex();
ete = etv[j];
lte = ltv[k] - edgeNode.getWeight();
if (ete == lte) {
System.out.println("<"+verList.get(j).getData()+","+verList.get(k).getData()+"> weight : "+edgeNode.getWeight());
}
edgeNode = edgeNode.getNext();
}
}
}
}
測試圖以及鄰接表
測試程序
public class CriticalPathMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
CriticalPath criticalPath = new CriticalPath();
criticalPath.buildAoeGraph();
criticalPath.CriticalPath();
}
}