最短路徑算法總結

                                                                                                                                                                                                             

1、目的;算法

求源點到其餘點之間的最短距離;數組

2、floyd算法;優化

(1)     假設起點爲A,終點爲B,則A到B的距離要麼是A直接到B,要麼A通過其餘節點到B,假設咱們通過的節點爲K,則最短路爲min(dist[A][B],dist[A][K]+dist[K][B])每次更新便可;隊列

 For (i=1;i<=n;i++)it

For (j=1;j<=n;j++)io

   For(k=1;k<=n;k++)原理

Dist[i][j]=min(dist[i][k]+dist[k][j],dist[i][j]);遍歷

(2) 以上代碼看起來彷佛沒錯,可是假設圖中有環的話,就會出現錯誤,好比A到B爲9,A 到C爲2 ,C到D爲2,D到B爲2,當i等於A,j等於B時,AB之間由於存在兩個節點,因此並無更新,即會出現錯誤。map

咱們能夠這樣來更新,根據中心點,來更新這個點兩端的距離,好比C爲中心點,則更新AD,而後D爲中心點,更新AB,CB,B爲中心點,更新AD,AC,以此類推。數據

For(k=1;k<=n;k++)

  For(i=1;i<=n;i++)

    For(j=1;j<=n;j++)

     Dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);

(3)路徑保存問題;

   咱們能夠設p[i][j]爲路徑ij中,j前面的那個節點;

 Void savePath(){

For (i=1;i<=n;i++)

  For(j=1;j<=n;j++)

    If(map[i][j]==MAX){

     P[i][j]=-1;

}else p[i][j]=i;

For(k=1;k<=n;k++)

  For (i=1;i<=n;i++)

    For (j=1;j<=n;j++)

    If(dist[i][j]>dist[i][k]+dist[k][j]){

         Dist[i][j]=dist[i][k]+dist[k][j];

         P[i][j]=p[k][j];

        

}

I=from;j=to;

Printf(「%d」,j);

While(p[i][j]!=from){

  Printf(「%d」,p[i][j]);

 J=p[i][j];

}

}

3、dijkstra算法

(1)   本算法是計算單源最短路的算法,即一個原點到其餘節點最短路的算法,原理爲,

假設已經計算出最短路的節點集合爲S,沒計算出來的爲V,最開始S只有一個源點,而後d[i]表明s到i的最短距離,首先咱們找出d中的最小值的節點t,而後將他加入S。

爲何這個時候d[t]必定是最短路徑呢?

由於假設t不是最短路,那麼必定存在另外的一條路更小,假設中間節點爲k則最短路必定爲d[k]+map[k][t];而d[t]爲最小值與題意不符,故d[t]必定爲最短路徑。

  (2)

void init(){

   int i,j;

   scanf("%d%d",&n,&m);

   for (i=1;i<=n;i++)

    for (j=1;j<=n;j++)

    map[i][j]=MAX;

   for (i=1;i<=m;i++)

   {

       int k,l,g;

       scanf("%d%d%d",&k,&l,&g);

       map[k][l]=g;

       map[l][k]=g;

   }

}

void dijkstra()

{

    int s,i,j;

    int v[10]={0};

    scanf("%d",&s);

    for (i=1;i<=n;i++)

        d[i]=map[s][i];

    v[s]=1;

    for (i=1;i<=n-1;i++)

    {

        int min=MAX,minj;

        for(j=1;j<=n;j++)

        if(!v[j]&&min>d[j])

        {

            min=d[j];

            minj=j;

        }

        v[minj]=1;

        for (j=1;j<=n;j++)

            if(!v[j]&&d[j]>d[minj]+map[minj][j])

        {

            d[j]=d[minj]+map[minj][j];

        }

    }

    for (i=1;i<=n;i++)

        printf("%d %d\n",i,d[i]);

}

(2)這個算法不能用於存在負權值的狀況。

如1->2 6

   1->3 5

  2 ->3 -2

此時算法會出錯。

4、Bellman-ford算法

(1)這個算法能夠用於處理負權值邊,原理:是利用每條邊進行鬆弛操做,我的認爲就是用每一條邊刷新一次最短路。

(2)咱們能夠用d數組來記錄最短路徑長度,初始化爲max,而後原點到原點的最短路爲零,而後遍歷n-1次,每次遍歷利用全部的邊來刷新最短路,要遍歷n-1次的緣由是由於最短路必定不包含環,假如包含正值環的話,去掉這個環,結果顯然更優,所以最短路徑中必定包含n-1條邊,而咱們每次刷新至少要刷新一條邊,假設沒有刷新了說明咱們最短路已經找到了。

(3)void bellman_ford()

{

    int s,i,j;

    scanf("%d",&s);

    for (i=1;i<=n;i++)

        d[i]=MAX;

    d[s]=0;

    for (i=1;i<=n-1;i++)

        for (j=1;j<=m;j++)

         if(d[edge[j].v]>d[edge[j].u]+edge[j].value)

    {

                d[edge[j].v]=d[edge[j].u]+edge[j].value;

                

    }

    int flag=0;

    //判斷是否存在權值爲負的環

    for (j=1;j<=m*2;j++)

        if(d[edge[j].v]+d[edge[j].u]+edge[j].value<0)

    {

        flag=1;

    }

    if(flag)printf("存在負環\n");

    else printf("不存在負環\n");

    for (i=1;i<=n;i++)

        printf("%d %d\n",i,d[i]);

}

4、spfa算法

(1)     算法是對bellman-ford算法的優化

(2)     將咱們每次優化後的節點放入隊列,根據隊列裏面的節點進行優化,減小了比較次數,同時減少了時間複雜度。

(3)     初始化咱們能夠定義一個數組d表示最短距離,而後c數組表明每一個點進入隊列的次數,當c超過n次的時候必定存在負環,要大於n而不是n-1次的緣由,我認爲應該防止只有一個點的狀況。

(4)     Spfa-bfs算法;

採用鄰接表儲存數據,

#include<stdio.h>

#define MAX 9999

int b[10][10]={0},w[10][10]={0};

int n,m;

int d[10];

void init()

{

    int i,j;

    scanf("%d%d",&n,&m);

    for (i=1;i<=m;i++)

    {

        int k,l,g;

        scanf("%d%d%d",&k,&l,&g);

        b[k][0]++;

        b[k][b[k][0]]=l;

        w[k][b[k][0]]=g;

    }

}

void spfa_bfs()

{

    int q[100];

    int h,t,i,j;

    h=1;t=1;

    int s;

    int flag=0;

    int v[10]={0};

    int c[10]={0};

    scanf("%d",&s);

     for (i=1;i<=n;i++) d[i]=MAX;

     d[s]=0;c[s]=1;q[1]=s;v[s]=1;

     while(h<=t)

     {

         int u=q[h];v[u]=0;

         for (j=1;j<=b[u][0];j++)

         {

             int y=b[u][j];

             if(d[y]>d[u]+w[u][j])

             {

                 d[y]=d[u]+w[u][j];

                 if(!v[y]){

                    t++;

                    q[t]=y;

                    c[y]++;

                    v[y]=1;

                    if(c[y]>n){

                            flag=1;

                        break;

                    }

                 }

             }

         }

         if(flag) break;

         h++;

     }

     for(i=1;i<=n;i++)

        printf("%d %d\n",i,d[i]);

     if(flag) printf("There exist fuhuan\n");

     else printf("There not exist fuhuan\n");

}

int main()

{

    init();

    spfa_bfs();

    return 0;

}

(5) 當咱們用隊列寬搜來判斷是否有負環時,會讓一個負環重複不少次纔會判斷出來,浪費了時間,而咱們用深搜能夠直接判斷假設有負環咱們會回到訪問過的節點,而且會執行鬆弛操做,而正環則不會進行鬆弛操做。

   int spfa_dfs(int u)

{

    vi[u]=1;

  int j;

  for (j=1;j<=b[u][0];j++)

  {

      int y=b[u][j];

      if(d[y]>d[u]+w[u][j]){

          d[y]=d[u]+w[u][j];

          if(!vi[y]){

              if(spfa_dfs(y)){

                return 1;

              }

          }else

          return 1;

          //當訪問過這個節點而且沿着這條路又能夠對當前節點進行鬆弛操做時,說明存在負環

      }

  }

  vi[u]=0;

  return 0;

}

(6)關於最短路徑的記錄問題,直接定義一個數組pre記錄前驅,在進行鬆弛操做時更改便可。

相關文章
相關標籤/搜索