圖的遍歷、拓撲排序、最短路徑算法


1.DFS(深度優先搜索)

深度優先搜索算法(Depth-First-Search),是搜索算法的一種。它沿着樹的深度遍歷樹的節點,盡可能深的搜索樹的分支。當節點v的所有邊都己被探尋過,搜索將回溯到發現節點v的那條邊的起始節點。這一過程一直進行到已發現從源節點可達的所有節點為止。如果還存在未被發現的節點,則選擇其中一個作為源節點並重復以上過程,整個進程反復進行直到所有節點都被訪問為止。DFS 屬於盲目搜索。

深度優先搜索是圖論中的經典算法,利用深度優先搜索算法可以產生目標圖的相應拓撲排序表,利用拓撲排序表可以方便的解決很多相關的圖論問題,如最大路徑問題等等。一般用堆數據結構來輔助實現 DFS 算法。

深度優先遍歷圖算法步驟:

  1.  訪問頂點v;

  2.  依次從v的未被訪問的鄰接點出發,對圖進行深度優先遍歷;直至圖中和v有路徑相通的頂點都被訪問;

  3.  若此時圖中尚有頂點未被訪問,則從一個未被訪問的頂點出發,重新進行深度優先遍歷,直到圖中所有頂點均被訪問過為止。

  上述描述可能比較抽象,舉個實例:

  DFS 在訪問圖中某一起始頂點 v 后,由 v 出發,訪問它的任一鄰接頂點 w1;再從 w1 出發,訪問與 w1 鄰 接但還沒有訪問過的頂點 w2;然后再從 w2 出發,進行類似的訪問,… 如此進行下去,直至到達所有的鄰接頂點都被訪問過的頂點 u 為止。

  接着,退回一步,退到前一次剛訪問過的頂點,看是否還有其它沒有被訪問的鄰接頂點。如果有,則訪問此頂點,之后再從此頂點出發,進行與前述類似的訪問;如果沒有,就再退回一步進行搜索。重復上述過程,直到連通圖中所有頂點都被訪問過為止。

//深度優先遍歷:   
void DFSTraverse ( Graph G )  
{  
  visited [0 .. G.vexnum-1] = false;   // 初始化訪問標志為未訪問(false)   
  for ( v = 0; v < G.vexnum; v ++ )  
    if ( ! visited[v] )  DFS ( G, v );  // 從未被訪問的頂點開始DFS   
}  
  
void DFS ( Graph G, int v )  
{  
  visit ( v );  visited [v] = true;   // 訪問頂點v並作標記   
  for ( w = FirstAdjVex(G,v); w >= 0; w = NextAdjVex(G,v,w) )  
    if ( ! visited[w] )  DFS ( G, w );  // 分別從每個未訪問的鄰接點開始DFS   
}  

Order in which the nodes are visited (one example).

2.BFS (廣度優先搜索)

廣度優先搜索算法(Breadth-First-Search),是一種圖形搜索算法。簡單的說,BFS 是從根節點開始,沿着樹(圖)的寬度遍歷樹(圖)的節點。如果所有節點均被訪問,則算法中止。BFS 同樣屬於盲目搜索。一般用隊列數據結構來輔助實現 BFS 算法。

  算法步驟:

  1.  首先將根節點放入隊列中。

  2.  從隊列中取出第一個節點,並檢驗它是否為目標。

    • 如果找到目標,則結束搜尋並回傳結果。
    • 否則將它所有尚未檢驗過的直接子節點加入隊列中。

  3.  若隊列為空,表示整張圖都檢查過了——亦即圖中沒有欲搜尋的目標。結束搜尋並回傳“找不到目標”。

  4.  重復步驟2。

//廣度優先遍歷:利用隊列(類似按層遍歷二叉樹)。
void BFSTraverse ( Graph G )  
{  
  visited [0 .. G.vexnum-1] = false;   // 初始化訪問標志為未訪問(false)   
  InitQueue ( Q );  
  for ( v = 0; v < G.vexnum; v++ )  
    if ( ! visited[v] )  
    {  
      // 從v出發廣度優先搜索   
      visit ( v );  visited [v] = true;  
      EnQueue ( Q, v );  
      while ( ! QueueEmpty(Q) )  
      {  
        DeQueue ( Q, u );  
        for ( w = FirstAdjVex(G,u); w >= 0; w = NextAdjVex(G,u,w) )  
          if ( ! visited[w] )  
          {  
            visit ( w );  visited [w] = true;  
            EnQueue ( Q, w );  
          }  
      }  
    }  
}  

3.拓撲排序(topological sort)

//拓撲排序:可以測試一個有向圖是否有環   
void Graph::topsort( )  
{  
    Queue<Vertex> q;  
    int counter = 0;  
    q.makeEmpty( );  
    for each Vertex v  
        if( v.indegree == 0 )  
            q.enqueue( v );  
    while( !q.isEmpty( ) )  
    {  
        Vertex v = q.dequeue( );  
        v.topNum = ++counter;  // Assign next number   
        for each Vertex w adjacent to v  
            if( --w.indegree == 0 )  
                q.enqueue( w );  
    }  
    if( counter != NUM_VERTICES )  
        throw CycleFoundException( );  
}  

4.Dijkstra算法(Dijkstra's algorithm)

迪傑斯特拉算法(Dijkstra’s algorithm)是由荷蘭計算機科學家艾茲赫爾·迪傑斯特拉提出。迪傑斯特拉算法使用了廣度優先搜索解決非負權有向圖的單源最短路徑問題,算法最終得到一個最短路徑樹。該算法常用於路由算法或者作為其他圖算法的一個子模塊。

該算法的輸入包含了一個有權重的有向圖 G,以及G中的一個來源頂點 S。我們以 V 表示 G 中所有頂點的集合。每一個圖中的邊,都是兩個頂點所形成的有序元素對。(u, v) 表示從頂點 u 到 v 有路徑相連。我們以 E 表示G中所有邊的集合,而邊的權重則由權重函數 w: E → [0, ∞] 定義。因此,w(u, v) 就是從頂點 u 到頂點 v 的非負權重(weight)。邊的權重可以想像成兩個頂點之間的距離。任兩點間路徑的權重,就是該路徑上所有邊的權重總和。已知有 V 中有頂點 s 及 t,Dijkstra 算法可以找到 s 到 t的最低權重路徑(例如,最短路徑)。這個算法也可以在一個圖中,找到從一個頂點 s 到任何其他頂點的最短路徑。對於不含負權的有向圖,Dijkstra 算法是目前已知的最快的單源最短路徑算法。

  算法步驟:

  1.  初始時令 S={V0},T={其余頂點},T中頂點對應的距離值

  若存在<V0,Vi>,d(V0,Vi)為<V0,Vi>弧上的權值

  若不存在<V0,Vi>,d(V0,Vi)為∞

  2.  從T中選取一個其距離值為最小的頂點W且不在S中,加入S

  3.  對其余T中頂點的距離值進行修改:若加進W作中間頂點,從 V0 到 Vi 的距離值縮短,則修改此距離值

  重復上述步驟2、3,直到S中包含所有頂點,即W=Vi 為止

算法實現:

/*迪傑斯特拉算法: 
求一個頂點到其他各頂點的最短路徑。 
算法: 
(a) 初始化:用起點v到該頂點w的直接邊(弧)初始化最短路徑,否則設為∞; 
(b) 從未求得最短路徑的終點中選擇路徑長度最小的終點u:即求得v到u的最短路徑; 
(c) 修改最短路徑:計算u的鄰接點的最短路徑,若(v,…,u)+(u,w)<(v,…,w),則以(v,…,u,w)代替。 
(d) 重復(b)-(c),直到求得v到其余所有頂點的最短路徑。 
特點:總是按照從小到大的順序求得最短路徑。 
*/  
//單源最短路徑dijkstra算法:s點到其他各頂點的最短路徑   
void Graph::dijkstra( Vertex s )  
{  
    for each Vertex v  
    {  
        v.dist = INFINITY;  
        v.known = false;  
    }  
    s.dist = 0;  
    for( ; ; )  
    {  
        Vertex v = smallest unknown distance vertex;  
        if( v == NOT_A_VERTEX )  
            break;  
        v.known = true;  
        for each Vertex w adjacent to v  
            if( !w.known )  
                if( v.dist + cvw < w.dist )  
                {  
                    // Update w   
                    w.dist = v.dist + cvw;  
                    w.path = v;  
                }  
    }  
}  

5.權值為1的單源最短路徑

//圖邊的權值為1,最短路徑:s點到其他各頂點的最短路徑   
void Graph::unweighted( Vertex s )  
{  
    for each Vertex v  
    {  
        v.dist = INFINITY;  
        v.known = false;  
    }  
    s.dist = 0;  
    for( int currDist = 0; currDist < NUM_VERTICES; currDist++ )  
        for each Vertex v  
            if( !v.known && v.dist == currDist )  
            {  
                v.known = true;  
                for each Vertex w adjacent to v  
                    if( w.dist == INFINITY )  
                    {  
                        w.dist = currDist + 1;  
                        w.path = v;  
                    }  
            }  
}  

使用隊列queue實現:

//圖邊的權值為1,最短路徑:s點到其他各頂點的最短路徑   
void Graph::unweighted( Vertex s )  
{  
    Queue<Vertex> q;  
    for each Vertex v  
        v.dist = INFINITY;  
    s.dist = 0;  
    q.enqueue( s );  
    while( !q.isEmpty( ) )  
    {  
        Vertex v = q.dequeue( );  
        for each Vertex w adjacent to v  
            if( w.dist == INFINITY )  
            {  
                w.dist = v.dist + 1;  
                w.path = v;  
                q.enqueue( w );  
            }  
    }  
}  

6.有負邊值的加權最短路徑

//有負邊值的加權最短路徑算法   
void Graph::weightedNegative( Vertex s )  
{  
    Queue<Vertex> q;  
    for each Vertex v  
        v.dist = INFINITY;  
    s.dist = 0;  
    q.enqueue( s );  
    while( !q.isEmpty( ) )  
    {  
        Vertex v = q.dequeue( );  
        for each Vertex w adjacent to v  
            if( v.dist + cvw < w.dist )  
            {  
                // Update w   
                w.dist = v.dist + cvw;  
                w.path = v;  
                if( w is not already in q )  
                    q.enqueue( w );  
            }  
    }  
}  

7.計算任意兩點間的最短路徑

/** 
 * Compute all-shortest paths. 
 * a contains the adjacency matrix with a[ i ][ i ] presumed to be zero. 
 * d contains the values of the shortest path. 
 * Vertices are numbered starting at 0; all arrays have equal dimension.  
 * A negative cycle exists if d[ i ][ i ] is set to a negative value. 
 * Actual path can be computed using path[ ][ ]. 
 * NOT_A_VERTEX is -1 
 */  
void allPairs( const matrix<int> & a, matrix<int> & d, matrix<int> & path )   
{  
    int n = a.numrows( );  
    // Initialize d and path   
    for( int i = 0; i < n; i++ )  
        for( int j = 0; j < n; j++ )  
        {  
            d[ i ][ j ] = a[ i ][ j ];  
            path[ i ][ j ] = NOT_A_VERTEX;  
        }  
    for( int k = 0; k < n; k++ )  
        // Consider each vertex as an intermediate   
        for( int i = 0; i < n; i++ )  
            for( int j = 0; j < n; j++ )  
                if( d[ i ][ k ] + d[ k ][ j ] < d[ i ][ j ] )  
                {  
                    // Update shortest path   
                    d[ i ][ j ] = d[ i ][ k ] + d[ k ][ j ];  
                    path[ i ][ j ] = k;  
                }  
}  

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM