圖的關(guān)鍵路徑

相關(guān)概念

AOE網(wǎng):在一個表示工程的帶權(quán)有向圖中醉拓,用頂點表示事件,用有向邊表示活動粥航,用邊上的權(quán)值表示活動的持續(xù)時間粟焊,這種有向圖的邊表示活動的網(wǎng)冤狡,我們稱之為AOE網(wǎng)(Activity On Edge Network)。

關(guān)鍵路徑與拓?fù)渑判虻膮^(qū)別:拓?fù)渑判蛑饕墙鉀Q一個工程能否順利進(jìn)行的問題项棠,關(guān)鍵路徑解決的是工程完成所需要的最短時間問題悲雳。換句話說,關(guān)鍵路徑問題需要在拓?fù)渑判虻幕A(chǔ)上得到完成工程的最短時間香追,因為工程首先必須是可以被完成的合瓢,也就是網(wǎng)中不存在環(huán)路時,才能進(jìn)一步討論工程完成所需的最短時間問題透典。

AOE網(wǎng)和AOV網(wǎng):AOV網(wǎng)是頂點表示活動的網(wǎng)晴楔,它只描述活動之間的制約關(guān)系;AOE網(wǎng)是用邊表示活動的網(wǎng)峭咒,邊上的權(quán)值表示活動持續(xù)的時間税弃。

關(guān)鍵路徑:從源點到匯點具有最大長度的路徑稱為關(guān)鍵路徑,在關(guān)鍵路徑上的活動叫關(guān)鍵活動讹语。只有縮短關(guān)鍵路徑上的關(guān)鍵活動時間才可以減少整個工期長度钙皮。


關(guān)鍵路徑算法參數(shù)介紹

事件的最早發(fā)生時間etv(earliest time of vertex):頂點Vn的最早發(fā)生時間;

事件的最晚發(fā)生時間ltv(latest tiem of vertex):頂點Vn的最晚發(fā)生時間;

活動的最早開工時間ete(earliest time of edge):弧En的最早發(fā)生時間;

活動的最晚開工時間lte(latest time of edge):弧En的最晚發(fā)生時間蜂科,也就是不推遲工期的最晚開工時間顽决。

關(guān)鍵路徑算法由etv和ltv求得ete和lte,當(dāng)ete與lte相等時表示活動沒有空閑時間短条,是關(guān)鍵活動。


etv和ltv數(shù)組的計算方法

etv數(shù)組計算公式.png

P[k]表示所有到達(dá)頂點Vk的弧的集合才菠,即Vk是弧的終點茸时。

ltv數(shù)組計算公式.png

S[k]表示所有從頂點Vk出發(fā)的弧的集合,即Vk是弧的起點赋访。


源代碼

帶權(quán)有向圖采用鄰接表的數(shù)據(jù)結(jié)構(gòu)表示

頂點表結(jié)點類

/**
 * 頂點表結(jié)點類
 * 
 * @author Shaw
 *
 */
class VertexNode {
    // 頂點入度
    private int in;
    // 頂點域
    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;
    }
}

邊表頂點類

/**
 * 邊表頂點類
 * @author Shaw
 *
 */
class EdgeNode {
    //鄰接點域可都,存儲該頂點對應(yīng)的下標(biāo)
    private int adjvex;
    //權(quán)值域
    private int weight;
    //指向下一個鄰接點
    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ù)組
    private int[] ltv; // (latest time of edge)事件最遲發(fā)生時間數(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)鍵路徑的計算
    // 求出事件的最早發(fā)生時間數(shù)組etv[]
    public void topologicalSortForCriticalPath() {
        Stack<Integer> stack = new Stack<>();
        etv = new int[verList.size()];
        stack2 = new Stack<>();
        int count = 0;
        // 初始化所有頂點表結(jié)點的入度值
        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)的頂點入度加1
                VertexNode vertexNode = verList.get(edgeNode.getAdjvex());
                vertexNode.setIn(vertexNode.getIn() + 1);
                edgeNode = edgeNode.getNext();
            }
        }
        // 將所有入度為0的頂點入棧
        for (int i = 0; i < verList.size(); i++) {
            if (verList.get(i).getIn() == 0) {
                stack.push(i);
            }
        }

        while (!stack.isEmpty()) {
            // 從棧中彈出一個入度為0的頂點
            int vertex = stack.pop();
            // 將出棧的頂點壓入stack2棧中用于關(guān)鍵路徑算法
            stack2.push(vertex);
            count++;
            // 獲取彈出的頂點指向的第一個邊結(jié)點
            EdgeNode edgeNode = verList.get(vertex).getFirstEdge();
            while (edgeNode != null) {
                // 獲取邊表結(jié)點的Adjvex值蚓耽,該值存儲對應(yīng)頂點表結(jié)點的下標(biāo)值
                int adjvex = edgeNode.getAdjvex();
                // 獲取邊表結(jié)點指向的頂點表結(jié)點
                VertexNode vertexNode = verList.get(adjvex);
                // 刪除邊結(jié)點渠牲,也就是將對應(yīng)的頂點表結(jié)點的入度減1
                vertexNode.setIn(vertexNode.getIn() - 1);
                // 如果該頂點表結(jié)點入度為0時壓棧
                if (vertexNode.getIn() == 0) {
                    stack.push(adjvex);
                }
                // 求各頂點事件的最早發(fā)生時間值
                if ((etv[vertex] + edgeNode.getWeight()) > etv[adjvex]) {
                    etv[adjvex] = etv[vertex] + edgeNode.getWeight();
                }
                // 獲取下一個邊表結(jié)點的值
                edgeNode = edgeNode.getNext();
            }
        }
    }

    // 關(guān)鍵路徑算法
    public void CriticalPath() {
        EdgeNode edgeNode;
        int i, stacktop, k, j;
        // 活動最早發(fā)生時間和最遲發(fā)生時間
        int ete, lte;
        topologicalSortForCriticalPath();
        System.out.println("etv數(shù)組:");
        for (i = 0; i < verList.size(); i++) {
            System.out.print(etv[i] + " ");
        }
        // 事件最晚發(fā)生時間
        ltv = new int[verList.size()];
        // 初始化ltv數(shù)組
        for (i = 0; i < verList.size(); i++) {
            ltv[i] = etv[verList.size() - 1];
        }
        // while循環(huán)計算具體的ltv數(shù)組的值
        while (!stack2.isEmpty()) {
            //從拓?fù)渑判虻慕Y(jié)果從后往前遍歷,因為先是從棧1中彈出后在壓入棧2的步悠,所以棧2的頂是拓?fù)渑判虻淖詈笠粋€結(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();
                }
            }
        }
    }

測試圖以及鄰接表

有向帶權(quán)無換圖.png

鄰接表圖.png

測試程序

public class CriticalPathMain {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        CriticalPath criticalPath = new CriticalPath();
        criticalPath.buildAoeGraph();
        criticalPath.CriticalPath();
    }
}

測試結(jié)果

關(guān)鍵路徑算法結(jié)果圖.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末签杈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鼎兽,更是在濱河造成了極大的恐慌答姥,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谚咬,死亡現(xiàn)場離奇詭異鹦付,居然都是意外死亡,警方通過查閱死者的電腦和手機择卦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門敲长,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秉继,你說我怎么就攤上這事潘明。” “怎么了秕噪?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵钳降,是天一觀的道長。 經(jīng)常有香客問我腌巾,道長遂填,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任澈蝙,我火速辦了婚禮吓坚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灯荧。我一直安慰自己礁击,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哆窿,像睡著了一般链烈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挚躯,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天强衡,我揣著相機與錄音,去河邊找鬼码荔。 笑死漩勤,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缩搅。 我是一名探鬼主播越败,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼硼瓣!你這毒婦竟也來了眉尸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤巨双,失蹤者是張志新(化名)和其女友劉穎噪猾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筑累,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡袱蜡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了慢宗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坪蚁。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖镜沽,靈堂內(nèi)的尸體忽然破棺而出敏晤,到底是詐尸還是另有隱情,我是刑警寧澤缅茉,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布嘴脾,位于F島的核電站,受9級特大地震影響蔬墩,放射性物質(zhì)發(fā)生泄漏译打。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一拇颅、第九天 我趴在偏房一處隱蔽的房頂上張望奏司。 院中可真熱鬧,春花似錦樟插、人聲如沸韵洋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搪缨。三九已至食拜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勉吻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工旅赢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留齿桃,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓煮盼,卻偏偏與公主長得像短纵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子僵控,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 關(guān)鍵路徑:在AOV網(wǎng)中香到,路徑上各個活動所持續(xù)的時間之和稱為路徑長度,從源點到匯點具有最大長度的路徑叫做關(guān)鍵路徑报破。 ...
    lkmc2閱讀 1,466評論 0 0
  • 第一章 緒論 什么是數(shù)據(jù)結(jié)構(gòu)悠就? 數(shù)據(jù)結(jié)構(gòu)的定義:數(shù)據(jù)結(jié)構(gòu)是相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合。 第二章...
    SeanCheney閱讀 5,771評論 0 19
  • 大部分內(nèi)容來自于《大話數(shù)據(jù)結(jié)構(gòu)》充易,代碼全部使用Swift實現(xiàn)梗脾。至于為什么抽風(fēng)寫這個???你懂的盹靴。 1.線性表 線性表...
    一劍孤城閱讀 81,827評論 12 111
  • 從小到大炸茧,我走路都很慢,慢著慢著也都習(xí)慣落在同行人的后邊稿静,看到的更多是朋友的背影梭冠,就像被落下的影子,永遠(yuǎn)只能藏在光...
    大黃sunny閱讀 118評論 0 0
  • 周末逛超市改备,你會發(fā)現(xiàn)控漠,某些商品的促銷已是常態(tài)化,甚至讓一部分顧客已經(jīng)養(yǎng)成了悬钳,某些商品润脸,不搞活動我不買,有了...
    山東老盧閱讀 397評論 0 1