圖的關(guān)鍵路徑

相關(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ì)算方法

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

P[k]表示所有到達(dá)頂點(diǎn)Vk的弧的集合,即Vk是弧的終點(diǎn)。

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

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();
                }
            }
        }
    }

測試圖以及鄰接表

有向帶權(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)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,427評論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,380評論 2 379

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

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