圖的遍歷與最短路徑算法


1. 圖的構造部分

采用鄰接矩陣存儲邊。節點編號為數字,從0~n-1,n為節點個數

class Graphs
{
public:
    Graphs(int n){ 
        m_VeticeNum = n;
        m_Edge.resize(n);
        m_Edge[0].resize(n); 
    }
  void InitEdge(vector<vector<int>> edge) { m_Edge = edge; }
private: int m_VeticeNum; //節點個數,為了方便,設定節點編號為0~m_VeticeNum-1 vector<vector<int>> m_Edge; };

初始化時,需要指定n,並傳入鄰接矩陣。

2. 圖的遍歷

對於圖的遍歷部分,原理參考圖的深度優先遍歷和廣度優先遍歷。下面是代碼實現:

int Graphs::findNeighbor(int v, int idx) //找到節點v,從idx之后的第一個鄰接點
{
    for (int i = idx + 1; i < m_VeticeNum; i++)
    {
        if (m_Edge[v][i] < INT_MAX && m_Edge[v][i] >0)
            return i;
    }
    return -1;
}  //找到從idx以后的v的下一個鄰接點

DFS:

void Graphs::DFS(int v)
{
    visited[v] = true;
    int w = findNeighbor(v, 0);//找到第一個鄰接點
    while (w != -1)
    {
        if (!visited[w])
        {
            cout << "訪問節點:" << w << endl;
            visited[w] = true;
            DFS(w);
        }
        w = findNeighbor(v, w);
    }
}

BFS:

void Graphs::BFS(int v)
{
    queue<int> q;
    q.push(v);
    visited[v] = true;
    cout << "訪問節點:" << v << endl;
    while (!q.empty())
    {
        int node = q.front();
        q.pop();
        //找鄰接點
        int w = findNeighbor(node, 0);
        while (w != -1)
        {
            if (!visited[w])
            {
                q.push(w);
                cout << "訪問節點:" << w << endl;
                visited[w] = true;
            }
            w = findNeighbor(node, w);
        }
    }

}

3. Dijkstra算法

Dijkstra算法原理參考最短路徑-Dijkstra和Floyd。其中的算法步驟個人認為下面的更好理解:

有兩個集合,一個是已經更新的有最短路徑的集合S,一個是待選擇的最短路徑集合U,初始時數組distance[]初始化為無窮,算法流程如下:

(1) 初始節點v,將v加入S,更新distance[v]=0,然后在所有U中尋找節點u1,u1與v相鄰且distance[v]+edge[v][u1]最小,當distance[v]+edge[v][u1]<distance[v]時,更新distance[u1];

(2) 將u添加到S,然后以新的節點u為跳板,在U中尋找新的節點u2,更新distance[u2]+edge[u1][u2]<distance[u2],且distance[u2]最小的節點u3

(3) 重復步驟2。

下面看具體代碼:

 1 vector<int> Graphs::Dijkstra(int v0)  //返回最短距離
 2 {
 3     vector<bool> S(m_VeticeNum, 0); //記錄已經求出最短路徑的節點
 4     vector<int> dis(m_VeticeNum, INT_MAX); //記錄最短距離
 5     S[v0] = true;
 6     int u = v0;
 7     dis[v0] = 0;
 8     for (int i = 1; i < m_VeticeNum; i++) //每次添加一個節點到S,共需要m_VeticeNum-1次
 9     {
10         //找出新的最短路徑點加入S
11         int minidx=0;
12         int minDis = INT_MAX;
13         for (int j = 0; j < m_VeticeNum; j++) 
14         {
15             if (!S[j])
16             {
17                 if(m_Edge[u][j]>0 && m_Edge[u][j] <INT_MAX && dis[u] + m_Edge[u][j] < dis[j])
18                     dis[j] = dis[u] + m_Edge[u][j]; //更新距離
19                 if (minDis > dis[j] && S[j]==false)
20                 {
21                     minDis = dis[j];
22                     minidx = j;
23                 }
24             }
25         }
26         u = minidx;
27         S[u] = true;
28     }
29     return dis;
30 }

 求出來的是從節點V0到所有其他節點的最短路徑。

如果要求出最短距離的路徑,再添加一個數組pre[],用於記錄前一個節點,只需要在18行更新距離后,記錄前一個節點,即pre[j]=u;

4. Floyd算法

Floyd算法原理參考Floyd。但是該博文沒有講路徑求解,路徑記錄參考博文Floyd算法求多元最短路徑

下面是代碼:

vector<vector<int>> Graphs::Floyd(void)
{
    vector<vector<int>> e(m_VeticeNum, vector<int>(m_VeticeNum,0));
    /* 如果需要記錄路徑,path[i][j]表示從i到j時到達j的前一步路徑
    vector<vector<int>> path(m_VeticeNum, vector<int>(m_VeticeNum, 0));
    for (int i = 0; i < m_VeticeNum; i++)
    {
        for (int j = 0; j < m_VeticeNum; j++)
            path[i][j] = i;
    }
    */
    e = m_Edge; //初始的路徑
    for (int k = 0; k < m_VeticeNum; k++) //對於每一個中間節點k
    {
        for (int i = 0; i < m_VeticeNum; i++)  //從節點i到節點j,中間經過k
        {
            for (int j = 0; j < m_VeticeNum; j++)
            {
                if (e[i][k] > 0 && e[i][k] < INT_MAX && e[k][j]>0 && e[k][j]<INT_MAX && e[i][j]>e[i][k] + e[k][j])
                {
                    e[i][j] = e[i][k] + e[k][j];
                    //path[i][j]=path[k][j]; 
                }
            }
        }
    }
    return e;
}

該函數求出來的是任意節點到i到節點j的最短路徑。

 

5. 相關例題

下面我們看一個最短路徑的例子:leetcode 743 網絡延遲時間。從題意可以分析出,該題目要求節點K到其他所有節點距離的最大值。采用Djikstra算法即能求出節點K到所有其他節點的最小距離。此處給出的輸入不是鄰接矩陣,而是所有邊,可以直接在所有邊上遍歷,並實時更新dis數組,同時每次添加一個新的節點到已訪問過的節點中,代碼如下:

int networkDelayTime(vector<vector<int>>& times, int N, int K) {
        vector<int> visited(N,false); //visited[i]表示節點i-1是否已訪問
        vector<int> dis(N,INT_MAX); //表示到各個節點的距離
        dis[K-1]=0;
        visited[K-1]=true;
        //初始化dis
        for(int i=0;i<size(times);i++)
        {
            if(times[i][0]==K) dis[times[i][1]-1]=times[i][2];
        }
        int u=K;
        for(int i=0;i<N-1;i++) //N個節點,每次添加一個到visited,需要N-1次
        {
            int mindis=INT_MAX;
            int idx=0;
            for(int j=0;j<size(times);j++)
            {
                if(times[j][0]==u && visited[times[j][1]-1]==false && dis[u-1]+times[j][2]<dis[times[j][1]-1]) 
                    dis[times[j][1]-1]=dis[u-1]+times[j][2]; //更新dis數組
                if(mindis>dis[times[j][1]-1] && visited[times[j][1] - 1] == false)
                {
                    mindis=dis[times[j][1]-1]; //尋找一個未訪問的節點添加到visited
                    idx=times[j][1];
                }
            }
            if(mindis==INT_MAX) //沒有距離可以更新,說明有不可達的節點
                    break;
            u=idx;
            visited[u-1]=true; //將新的最近節點添加到visited
        }
        int maxdis=0;
        for(int i=0;i<N;i++)
            if(dis[i]>maxdis) maxdis=dis[i];
        return maxdis==INT_MAX? -1:maxdis;
    }

 


免責聲明!

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



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