Dijkstra和Prim算法的區別


Dijkstra和Prim算法的區別

1.先說說prim算法的思想:

眾所周知,prim算法是一個最小生成樹算法,它運用的是貪心原理(在這里不再證明),設置兩個點集合,一個集合為要求的生成樹的點集合A,另一個集合為未加入生成樹的點B,它的具體實現過程是:

第1步:所有的點都在集合B中,A集合為空。

第2步:任意以一個點為開始,把這個初始點加入集合A中,從集合B中減去這個點(代碼實現很簡單,也就是設置一個標示數組,為false表示這個點在B中,為true表示這個點在A中),尋找與它相鄰的點中路徑最短的點,如后把這個點也加入集合A中,從集合B中減去這個點(代碼實現同上)。

第3步:集合A中已經有了多個點,這時兩個集合A和B,只要找到A集合中的點到B集合中的點的最短邊,可以是A集合中的與B集合中的點的任意組合,把這條最短邊有兩個頂點,把在集合B中的頂點加入到集合A中,(代碼實現的時候有點技巧,不需要枚舉所有情況,也就是更新操作)。

第4步:重復上述過程。一直到所有的點都在A集合中結束。

2.說說dijkstra算法的過程:

這個算法的過程有比prim算法的過程稍微多一點點步驟,但是思想確實巧妙的,也是貪心原理,它的目的是求某個源點到目的點的最短距離,總的來說,dijkstra算法也就是求某個源點到目的點的最短路,求解的過程也就是求源點到整個圖的最短距離,次短距離,第三短距離等等(這些距離都是源點到某個點的最短距離)。。。。。求出來的每個距離都對應着一個點,也就是到這個點的最短距離,求的也就是原點到所有點的最短距離,並且存在了一個二維數組中,最后給出目的點就能直接通過查表獲得最短距離。

第1步:以源點(假設是s1)為開始點,求最短距離,如何求呢? 與這個源點相鄰的點與源點的距離全部放在一個數組dist[]中,如果不可達,dist[]中為最大值,這里說一下,為什么要是一維數組,原因是默認的是從源點到這個一維數組下標的值,只需要目的點作為下標就可以,這時從源點到其他點的最短的“一”條路徑有了,只要選出dist[]中最小的就行(得到最短路徑的另一個端點假設是s2)。

第2步:這時要尋找源點(假設是s1)到另外點的次短距離,這個距離或者是dist[]里面的值,或者是從第1步中選擇的那個最短距離 + 從找到點(假設是s2)出發到其他點的距離(其實這里也是一個更新操作,更新的是dist[]里面的值),如果最短距離 + 從這點(假設是s2)到其他點的距離,小於dist[]里面的值,就可以更新dist[]數組了,然后再從dist[]數組中選一個值最小的,也就是第“二”短路徑(次短路徑)。

第3步:尋找第“三”短路徑,這時同上,第二短路徑的端點(s3)更新與之相鄰其他的點的dist[]數組里面的值。

第4步:重復上述過程n - 1次(n指的是節點個數),得出結果,其實把源點到所有點的最短路徑求出來了,都填在了dist[]表中,要找源點到哪個點的最短路,就只需要查表了。

 

 

 

Dijkstra模版:

 

//dijkstra算法模版,狄傑斯特拉算法模板,單源最短路模板
//dijkstra算法模版

#include <stdio.h>
#include <string.h>
#define MaxN 1001
#define MaxInt 200000000
int map[MaxN][MaxN],dist[MaxN];
bool mark[MaxN];
int n,start,end;
int main()
{
    int i,j,min1,minj,temp;

//****************輸入**********************
    scanf("%d%d%d",&n,&start,&end);
    for (i=1;i<=n;i++) 
        for (j=1;j<=n;j++)
            scanf("%d",&map[i][j]); 
//******************************************
//*****************初始化**********************
    for (i=1;i<=MaxN;i++)
        dist[i]=MaxInt;
    memset(mark,0,sizeof(mark));
    dist[start]=0;                  //把起點並入集合,搜索時就可以從起點尋找到第一條最短的邊了
//*********************************************
    for (i=1;i<=n-1;i++)
    {
        min1=MaxInt;
        for (j=1;j<=n;j++) //查找到原集合的最短的邊
        {
            if (!mark[j]&&dist[j]<min1)
            {
                min1=dist[j];
                minj=j;
            }
        }
        mark[minj]=1;
        for (j=1;j<=n;j++) //每並入一個點都要對原來的邊進行修正,保證任意時刻源點到目標點的距離都是最短的。
        {
            if (!mark[j]&&map[minj][j]>0)
            {
                temp=dist[minj]+map[minj][j];
                if (temp<dist[j]) 
                    dist[j]=temp;
            }
        }
    }
//***********輸出***************
    printf("%d\n",dist[end]);
//******************************
    return 0;
}
View Code

 

 

Prim算法中尋找的是下一個與MST中任意頂點相距最近的頂點;  
Dijkstra算法尋找的是下一個離給定頂點(單源)最近的頂點。
另外,當有兩條具有同樣的最小權值的邊可供選擇時,任選一條即可,所以構造的MST不是惟一的。
Prim算法和Dijkstra算法十分相似,惟一的區別是: Prim算法要尋找的是離已加入頂點距離最近的頂點;
Dijkstra算法是尋找離固定頂點距離最近的頂點。
所以Prim算法的時間復雜度分析與Dijkstra算法相同,都是 O(|V^2|)

【拓】:Kruskal算法:http://baike.baidu.com/link?url=MchMLaw4a3nLu3bWSoEUEak-DYbM8n0H27ANKE5-Gv_frudxAvGfsqdpNRqDtdB0

克魯斯卡爾(Kruskal)算法(只與邊相關)

算法描述:克魯斯卡爾算法需要對圖的邊進行訪問,所以克魯斯卡爾算法的時間復雜度只和邊又關系,可以證明其時間復雜度為O(eloge)。

算法過程:

1.將圖各邊按照權值進行排序

2.將圖遍歷一次,找出權值最小的邊,(條件:此次找出的邊不能和已加入最小生成樹集合的邊構成環),若符合條件,則加入最小生成樹的集合中。

不符合條件則繼續遍歷圖,尋找下一個最小權值的邊。

3.遞歸重復步驟1,直到找出n-1條邊為止(設圖有n個結點,則最小生成樹的邊數應為n-1條),算法結束。得到的就是此圖的最小生成樹。

克魯斯卡爾(Kruskal)算法因為只與邊相關,則適合求稀疏圖的最小生成樹。而prime算法因為只與頂點有關,所以適合求稠密圖的最小生成樹。

代碼:

#include<iostream>
#include<cstring>
#include<string>

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 1000
int father[MAX], son[MAX];
int v, l;

typedef struct Kruskal //存儲邊的信息
{
    int a;
    int b;
    int value;
};

bool cmp(const Kruskal & a, const Kruskal & b)
{
    return a.value < b.value;
}

int unionsearch(int x) //查找根結點+路徑壓縮
{
    return x == father[x] ? x : unionsearch(father[x]);
}

bool join(int x, int y) //合並
{
    int root1, root2;
    root1 = unionsearch(x);
    root2 = unionsearch(y);
    if(root1 == root2) //為環
        return false;
    else if(son[root1] >= son[root2])
        {
            father[root2] = root1;
            son[root1] += son[root2];
        }
        else
        {
            father[root1] = root2;
            son[root2] += son[root1];
        }
    return true;
}

int main()
{
    int ncase, ltotal, sum, flag;
    Kruskal edge[MAX];
    scanf("%d", &ncase);
    while(ncase--)
    {
        scanf("%d%d", &v, &l);
        ltotal = 0, sum = 0, flag = 0;
        for(int i = 1; i <= v; ++i) //初始化
        {
            father[i] = i;
            son[i] = 1;
        }
        for(int i = 1; i <= l ; ++i)
        {
            scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].value);
        }
        sort(edge + 1, edge + 1 + l, cmp); //按權值由小到大排序
        for(int i = 1; i <= l; ++i)
        {
            if(join(edge[i].a, edge[i].b))
            {
                ltotal++; //邊數加1
                sum += edge[i].value; //記錄權值之和
                cout<<edge[i].a<<"->"<<edge[i].b<<endl;
            }
            if(ltotal == v - 1) //最小生成樹條件:邊數=頂點數-1
            {
                flag = 1;
                break;
            }
        }
        if(flag) printf("%d\n", sum);
        else printf("data error.\n");
    }
    return 0;
}
View Code

 

 

 

 


免責聲明!

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



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