圖的最短路徑——dijkstra算法和Floyd算法


dijkstra算法

  求某一頂點到其它各個頂點的最短路徑;已知某一頂點v0,求它頂點到其它頂點的最短路徑,該算法按照最短路徑遞增的順序產生一點到其余各頂點的所有最短路徑。

對於圖G={V,{E}};將圖中的頂點分為兩組:

  第一組S:求出已知頂點的最短路徑的集合

  第二組V-S:尚未求出最短路徑的頂點集合(開始為V-{v0}的全部頂點)

該算法將最短路徑以遞增順序逐個將第二組頂點加入到第一組頂點中,直到所有的頂點都被加入到第一組頂點集S為止。

dijkstra算法和最小生樹中的prim算法類似,都是把頂點看做集合,向所求集合中加點

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int INF=0x3f3f;
class Graph
{
    private:
        int num;
        int e;
        vector<vector<int> > arr;//存儲圖的鄰接矩陣 
        vector<bool> visit;//標記該結點是否用過 
        vector<int> path;//從v0到其他結點的最短路徑 
    public:
        Graph();
        void dijkstra(const int &i);
};
Graph::Graph()
{
    cout<<" num"<<endl;
    cin>>num;
    cout<<" e"<<endl;
    cin>>e;
    
    visit.resize(num,false);
    path.resize(num);
    
    arr.resize(num);
    for(int i=0;i<num;++i)
        arr.at(i).resize(num,INF);
    
    cout<<" 邊的起始點和終點&&權值"<<endl;
    pair<int,int> p;
    for(int i=0;i<e;++i) 
    {
        cin>>p.first>>p.second;
        cin>>arr.at(p.first-1).at(p.second-1);
    }
}
void Graph::dijkstra(const int &index)
{
    //首先存儲的是index結點到其他節點的最短路徑的值 
    for(int i=0;i<num;++i) 
        path.at(i)=arr.at(index-1).at(i);
    //初始化visit
    visit.at(index-1)=true;
    
    for(int check=0;check<num-1;++check)
    {
        int Min=INF,flag=0;
        for(int i=0;i<num;++i)
        {
            if(!visit.at(i)&&path.at(i)<Min)
            {
                flag=i;
                Min=path.at(i);
            }
        }
        visit.at(flag)=true;
        for(int i=0;i<num;++i)//如果由於v0結點的加入導致index結點到其它接點的值變小更新path 
        {
            if(path.at(i)>path.at(flag)+arr.at(flag).at(i))
                path.at(i)=path.at(flag)+arr.at(flag).at(i);
        }
    }
    for(int i=0;i<num;++i)
        cout<<path.at(i)<<"\t";
    cout<<endl;
}
int main()
{
    Graph g;
    g.dijkstra(1);
    return 0;
}

 floyd算法

  如果要讓任意兩點(例如從頂點a點到頂點b)之間的路程變短,只能引入第三個點(頂點k),並通過這個頂點k中轉即a->k->b,才可能縮短原來從頂點a點到頂點b的路程。那么這個中轉的頂點k是1~n中的哪個點呢?甚至有時候不只通過一個點,而是經過兩個點或者更多點中轉會更短。

  當任意兩點之間不允許經過第三個點時,這些城市之間最短路程就是初始路程

  一:假如現在只允許經過1號頂點,求任意兩點之間的最短路程,只需判斷e[i][1]+e[1][j]是否比e[i][j]要小即可。e[i][j]表示的是從i號頂點到j號頂點之間的路程。e[i][1]+e[1][j]表示的是從i號頂點先到1號頂點,再從1號頂點到j號頂點的路程之和。其中i是1~n循環,j也是1~n循環

for (i = 1; i <= n; i++)
        for (j = 1; j <= n; j++)
        {
            if (e[i][j] > e[i][1] + e[1][j])
                e[i][j] = e[i][1] + e[1][j];
        }

  在只允許經過1號頂點的情況下,任意兩點之間的最短路程更新為

  二:接下來繼續求在只允許經過1和2號兩個頂點的情況下任意兩點之間的最短路程,我們需要在只允許經過1號頂點時任意兩點的最短路程的結果下,再判斷如果經過2號頂點是否可以使得i號頂點到j號頂點之間的路程變得更短。即判斷e[i][2]+e[2][j]是否比e[i][j]要小,

//經過1號頂點
for(i=1;i<=n;i++)  
    for(j=1;j<=n;j++)  
        if (e[i][j]>e[i][1]+e[1][j])  
            e[i][j]=e[i][1]+e[1][j];  
            
//經過2號頂點
for(i=1;i<=n;i++)  
    for(j=1;j<=n;j++) 
        if(e[i][j] > e[i][2]+e[2][j])
            e[i][j]=e[i][2]+e[2][j];

  在只允許經過1和2號頂點的情況下,任意兩點之間的最短路程更新為:

  三:最后允許通過所有頂點作為中轉

  最開始只允許經過1號頂點進行中轉,接下來只允許經過1和2號頂點進行中轉……允許經過1~n號所有頂點進行中轉,求任意兩點之間的最短路程。用一句話概括就是:從i號頂點到j號頂點只經過前k號點的最短路程。

for(k=1;k<=n;k++)//允許中轉的k個結點 
    for(i=1;i<=n;i++)//源地點i 
        for(j=1;j<=n;j++)//目標地點j 
            if(e[i][j]>e[i][k]+e[k][j])  
                e[i][j]=e[i][k]+e[k][j];

code:求固定兩地點的最短路徑

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int INF=0x3f3f;
class Graph
{
    private:
        int num;
        int e;
        vector<vector<int> > arr;//存儲圖的鄰接矩陣 
        vector<int> path;//從v0到其他結點的最短路徑 
    public:
        Graph();
        void floyd(const int &begin,const int &end);
};
Graph::Graph()
{
    cout<<" num"<<endl;
    cin>>num;
    cout<<" e"<<endl;
    cin>>e;
    
    path.resize(num);
    
    arr.resize(num);
    for(int i=0;i<num;++i)
        arr.at(i).resize(num,INF);
    
    cout<<" 邊的起始點和終點&&權值"<<endl;
    pair<int,int> p;
    for(int i=0;i<e;++i) 
    {
        cin>>p.first>>p.second;
        cin>>arr.at(p.first-1).at(p.second-1);
    }
}
void Graph::floyd(const int &begin,const int &end)
{
    //允許經過的中轉點;k==0,經過第一個中轉點,==1經過第二個中轉點(此時已經進過兩個中轉點
    //最多可以經過n個中轉點)
    for(int k=0;k<num;++k) 
        if(arr.at(begin-1).at(end-1)>arr.at(begin-1).at(k)+arr.at(k).at(end-1))
            arr.at(begin-1).at(end-1)=arr.at(begin-1).at(k)+arr.at(k).at(end-1);
                    
    cout<<arr.at(begin-1).at(end-1)<<endl;
}
int main()
{
    Graph g;
    g.floyd(1,5);
    return 0;
}

 


免責聲明!

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



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