六、圖的應(yīng)用

最短路徑

舉了個(gè)地鐵圖的例子
網(wǎng)絡(luò)中兩定點(diǎn)間的所有路徑中,邊權(quán)值之和最小的那條即為最短路徑shortest path
source -> destination
分為:

無(wú)權(quán)單源

void Unweighted ( Vertex S ){ //無(wú)權(quán)圖的單源最短路徑
    /*
    先初始下列初始化
    dist[W] = S到W的最短距離
    dist[S] = 0; //S自己到自己的距離為0
    path[W] = S到W的路上經(jīng)過的頂點(diǎn)
    */
    Enqueue(S, Q);    //源結(jié)點(diǎn)入隊(duì)
    while(!IsEmpty(Q)){ 
        V = Dequeue(Q);     //出隊(duì),此時(shí)最短路已被找到
        for ( V 的每個(gè)鄰接點(diǎn) W )
            if ( dist[W]==-1 ) {    //假設(shè)-1為未訪問過的初始值,于是訪問它
                dist[W] = dist[V]+1;    //前結(jié)點(diǎn)到S距離+1
                path[W] = V;    //V是必經(jīng)的頂點(diǎn),經(jīng)過堆棧處理可打印出最短路徑經(jīng)過的結(jié)點(diǎn)
                Enqueue(W, Q);
            }
    }
}
//用鄰接表存儲(chǔ)T = O(|V|+|E|)

有權(quán)單源

Dijkstra算法
以起始點(diǎn)為中心向外層層擴(kuò)展,直到擴(kuò)展到終點(diǎn)為止

void Dijkstra( Vertex s ){ 
    /*
    S = { 源點(diǎn)s + 已經(jīng)確定了最短路徑的頂點(diǎn)vi }
    dist[v] = s到v[僅經(jīng)過S中的各頂點(diǎn)]的最短路徑長(zhǎng)度,即路徑{s->(vi屬于S)->v}的最小長(zhǎng)度
    dist[w] = min{dist[w], dist[v] + <v,w>的權(quán)重}
    dist[s] = 0;
    */
    while (1) {
    V = 未收錄頂點(diǎn)中dist最小者;
    if ( 這樣的V不存在 )
        break;
    collected[V] = true;    //V被收錄
    for ( V 的每個(gè)鄰接點(diǎn) W )
        if ( collected[W] == false )    //W未被收錄
        if ( dist[V]+E<V,W> < dist[W] ) {   //dist被初始化為正無(wú)窮
        dist[W] = dist[V] + E<V,W> ;
        path[W] = V;
        }
    }
} /* 不能解決有負(fù)邊的情況 */
// 法1.直接掃描所有未收錄頂點(diǎn) – O( |V| )
// T = O( |V|*|V| + |E| ) 對(duì)于稠密圖效果好
// 法2.將dist存在最小堆中 – O( log|V| )
// 更新dist[w]的值 – O( log|V| )
// T = O( |V| log|V| + |E| log|V| ) = O( |E| log|V| ) 對(duì)于稀疏圖效果好

有權(quán)多源

法1.對(duì)于稀疏圖效果好
將單源最短路算法調(diào)用|V|遍,對(duì)每一個(gè)頂點(diǎn)調(diào)用Dijkstra算法
T = O( |V||V||V| + |E||V|)
法2.
Floyd算法*

void Floyd(){
//求多源最短路徑
    for ( i = 0; i < N; i++ )
        for( j = 0; j < N; j++ ) {
        D[i][j] = G[i][j];  /*Dij為i到j(luò)的最小距離;初始化為其鄰接矩陣,對(duì)角線為0,其他為兩邊的權(quán)值,不相鄰為正無(wú)窮*/
        path[i][j] = -1;    //最短路徑初始化-1
        }
    for( k = 0; k < N; k++ )    /*D0 -> Dk ,包含k個(gè)結(jié)點(diǎn)時(shí),i到j(luò)的最小值,當(dāng)k從0->n-1則說(shuō)明遍歷了整個(gè)圖*/
        for( i = 0; i < N; i++ )
            for( j = 0; j < N; j++ )
                if( D[i][k] + D[k][j] < D[i][j] ) { /*若k加入后是更小的值,則必是i到k和k到j(luò)最短路徑的和*/
                    D[i][j] = D[i][k] + D[k][j];    /*若更小則更新*/
                    path[i][j] = k; // i -> k -> j
                }
}
// T = O( |V|3 ) 對(duì)于稠密圖效果好

最小生成樹

:無(wú)回路,V個(gè)頂點(diǎn)且有V-1條邊
生成樹:包含全部頂點(diǎn),V-1條邊全在圖里
最小生成樹:邊的權(quán)重和最小
思路:貪心算法
每一步都是最好的

Prim算法

void Prim(){ 
    MST = {s};  //初始化一棵最小生成樹,選了個(gè)根結(jié)點(diǎn)S
    while (1) {
        V = 未收錄頂點(diǎn)中dist最小者;  //頂點(diǎn)V到最小生成樹(上的所有頂點(diǎn))的最小距離dist
        if ( 這樣的V不存在 )  //沒有沒收錄的頂點(diǎn)了,或多有沒收錄的頂點(diǎn)都沒邊了(不連通)
            break;
        將V收錄進(jìn)MST: dist[V] = 0;  //變成樹本身,距離為0
        for ( V 的每個(gè)鄰接點(diǎn) W )
            if ( dist[W]!=0 )    //說(shuō)明W未被收錄
                if ( E(V,W) < dist[W] ){
                    dist[W] = E(V,W) ;
                    parent[W] = V;  //V可能是W的parent?
                }
    }
    if ( MST中收的頂點(diǎn)不到|V|個(gè) )   //(不連通)
        Error ( “生成樹不存在” );
}//T = O( V*V )稠密圖合算

Kruskal算法

思路:把森林合并成樹
初始每個(gè)頂點(diǎn)都是一棵樹,通過不斷收邊,兩棵樹并成一顆樹,直至全部并成一棵樹。但要注意保持最小生成樹的3個(gè)性質(zhì)(前面有提到)

void Kruskal ( Graph G ){ 
    MST = { } ; //一開始一條邊都沒有
    while ( MST 中不到 |V|-1 條邊 && E 中還有邊 ) {  //E是所有邊的集 最壞情況O(|V|-1)次
        從 E 中取一條權(quán)重最小的邊 E(v,w) ; /*最小堆 O(logE)取出最小邊*/
        將 E(v,w)從 E 中刪除;
        if ( E(V,W)不在 MST 中構(gòu)成回路)    /*并查集 V和W分別屬于不同集合則不會(huì)構(gòu)成回路*/
            將 E(V,W) 加入 MST;
        else
            徹底無(wú)視 E(V,W);    
    }
    if ( MST 中不到 |V| ?1 條邊 )
        Error ( “生成樹不存在” );
}
//T = O( |E|*log|E| )稀疏圖合算 即E約等于V時(shí),約定于T = O( |V|*log|V| )比Prime快一點(diǎn)

拓?fù)浣Y(jié)構(gòu)

如果圖中從V到W有一條有向路徑,則V一定排在W之前。滿足此條件的頂點(diǎn)序列,稱為一個(gè)拓?fù)湫颉?br> AOV必須是有向無(wú)環(huán)圖DAG。

<pre>void TopSort(){
   for ( cnt = 0; cnt < |V|; cnt++ ) {
       V = 未被輸出的 && 入度為0的頂點(diǎn); //普通方法O(|V|),則整體T=O(V*V)。
       if ( 這樣的V不存在 ) {    //必定有回路
           Error ( “圖中有回路” );
           break;
       }
       輸出V,或者記錄V的輸出序號(hào);
       for ( V 的每個(gè)鄰接點(diǎn) W )
           Indegree[W]––; //入度-1,即V-W這條邊去掉
   }
}//普通方法O(|V|),則整體T=O(|V|*|V|)。
</pre> 
<pre>//改進(jìn)將入度變?yōu)?的頂點(diǎn)放到一個(gè)容器里,下次從容器里取出即可,加快V的查找
void TopSort(){ 
    for ( 圖中每個(gè)頂點(diǎn) V )
        if ( Indegree[V]==0 )
            Enqueue( V, Q );    //這里的容器用隊(duì)列
    while ( !IsEmpty(Q) ) {
        V = Dequeue( Q );   //容器里取出入度為0的頂點(diǎn)
        輸出V,或者記錄V的輸出序號(hào); 
        cnt++;  //記錄輸出的頂點(diǎn)個(gè)數(shù)
        for ( V 的每個(gè)鄰接點(diǎn) W )
            if ( ––Indegree[W]==0 ) //鄰接點(diǎn)入度-1,但要檢查是否減完后變?yōu)?
                Enqueue( W, Q );    //若為0再放入容器
    }
    if ( cnt != |V| )   //還有頂點(diǎn)留在圖里
        Error( “圖中有回路” );
}//T=O(|V|+|E|) 可用來(lái)檢測(cè)DAG
</pre> 

拓?fù)渑判虻膽?yīng)用

關(guān)鍵路徑問題

  • AOV網(wǎng)絡(luò)(Activity On vertex)
  • AOE網(wǎng)絡(luò) (Activity On Edge)

這里討論AOE網(wǎng)絡(luò),關(guān)鍵路徑由絕對(duì)不允許延誤的活動(dòng)組成的路徑,沒有機(jī)動(dòng)時(shí)間的路徑組成的路徑就是關(guān)鍵路徑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第一章 緒論 什么是數(shù)據(jù)結(jié)構(gòu)? 數(shù)據(jù)結(jié)構(gòu)的定義:數(shù)據(jù)結(jié)構(gòu)是相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合。 第二章...
    SeanCheney閱讀 6,002評(píng)論 0 19
  • 圖是一種比線性表和樹更復(fù)雜的數(shù)據(jù)結(jié)構(gòu),在圖中,結(jié)點(diǎn)之間的關(guān)系是任意的,任意兩個(gè)數(shù)據(jù)元素之間都可能相關(guān)。圖是一種多對(duì)...
    Alent閱讀 2,420評(píng)論 1 22
  • https://zh.visualgo.net/graphds 淺談圖形結(jié)構(gòu)https://zh.visualgo...
    狼之獨(dú)步閱讀 4,418評(píng)論 0 0
  • -DFS(Depth First Search):深度優(yōu)先搜索 訪問完一個(gè)頂點(diǎn)的所有鄰接點(diǎn)之后,會(huì)按原路返回,對(duì)應(yīng)...
    Spicy_Crayfish閱讀 2,947評(píng)論 1 0
  • 現(xiàn)實(shí)生活中有很大一類問題可以用簡(jiǎn)潔明了的圖論語(yǔ)言來(lái)描述,可以轉(zhuǎn)化為圖論問題。 相關(guān)定義 圖可以表示為G=(V, E...
    芥丶未央閱讀 1,830評(píng)論 0 7

友情鏈接更多精彩內(nèi)容