1. 程式人生 > >圖之從一個頂點到其餘各個頂點的最短路徑(有向圖)

圖之從一個頂點到其餘各個頂點的最短路徑(有向圖)

目錄

  1. 從一個頂點到其餘各個頂點最短路徑的簡介
  2. 舉例以及詳細分析
  3. 程式碼塊
  4. 測試結果

從一個頂點到其餘各個頂點最短路徑的簡介(又名單元最短路徑)

1.定義概覽

Dijkstra(迪傑斯特拉)演算法是典型的單源最短路徑演算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。Dijkstra演算法是很有代表性的最短路徑演算法,在很多專業課程中都作為基本內容有詳細的介紹,如資料結構,圖論,運籌學等等。注意該演算法要求圖中不存在負權邊。

問題描述:在無向圖 G=(V,E) 中,假設每條邊 E[i] 的長度為 w[i],找到由頂點 V0 到其餘各點的最短路徑。(單源最短路徑)

演算法思想:設G=(V,E)是一個帶權有向圖,把圖中頂點集合V分成兩組,第一組為已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,以後每求得一條最短路徑 , 就將加入到集合S中,直到全部頂點都加入到S中,演算法就結束了),第二組為其餘未確定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點v到S中各頂點的最短路徑長度不大於從源點v到U中任何頂點的最短路徑長度。此外,每個頂點對應一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點為中間頂點的當前最短路徑長度。

舉例以及詳細分析

這裡寫圖片描述
a.初始時,S只包含源點,即S={v},v的距離為0。U包含除v外的其他頂點,即:U={其餘頂點},若v與U中頂點u有邊,則(u,v)正常有權值,若u不是v的出邊鄰接點,則(u,v)權值為∞。

b.從U中選取一個距離v最小的頂點k,把k,加入S中(該選定的距離就是v到k的最短路徑長度)。

c.以k為新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改後的距離值的頂點k的距離加上邊上的權。

d.重複步驟b和c直到所有頂點都包含在S中。

這裡寫圖片描述
首先先定義三個輔助陣列:
int s[MAX]用來表示這個頂點是否加入了最短距離路徑。
int dist[MAX]用來表示起始點到這個頂點的最短路徑是多少。
int path[MAX]用來表示這個頂點的上一個頂點是什麼,即表示的是路徑。

初始化時 假設其實點是v
s[MAX]存放的都是0,表示還未有頂點加入最短路徑。dist[MAX]存放的是起始點與其鄰接點的最短距離即g->edges[v][j],不是起始點的鄰接點的頂點用∞表示。path[MAX]存放的也都是v,表示都是從起始點v開始的,如果頂點與起始點沒有直接聯絡則用-1表示。
又因為起始點為v,所以初始化的最後s[v]=1,path[v]=0;

之後在dist[]中找到(dist[i]最小的並且s[i]==0)的頂點,作為最短路徑的下一個頂點(下標為u),並將其s[u]變為1。
找到下一個頂點後,圖的最短路徑可能發生了相應的變化;將新頂點與其他s[i]!=0的頂點進行聯立,尋找他們之間的最小路徑並改變其相應的path[],dist[]的值。
這裡寫圖片描述

在最後的輸出函式中必須嚴格遵守遞迴規則
這裡寫圖片描述
紅圈中的順序不能顛倒否則輸出的數值中
這裡寫圖片描述
劃線部分會輸出逆序(請自行體會);

程式碼塊

#include<stdio.h>
#include<stdlib.h>
#define MAXV 100
#define LIMITLESS 9999

typedef struct
{
    int no;  //頂點編號 
    int info; //頂點其他資訊 
} VertexType;

typedef struct
{
    int n;
    int e;//定點數,邊數 
    int edges[MAXV][MAXV];//鄰接矩陣的陣列表現
    VertexType vexs[MAXV]; //頂點資訊 
}MGraph;

void creat(MGraph *G)
{
    int i, j, k, w;
    int start, end;
    printf("請輸入頂點數和邊數:\n");
    scanf("%d%d", &(G->n), &(G->e));
    getchar();
    printf("請輸入頂點資訊:\n");
    for (i = 0; i<G->n; i++)
    {
        scanf("%d%d", &(G->vexs[i].no), &(G->vexs[i].info));
    }
    for (i = 0; i<G->n; i++)
    {
        for (j = 0; j<G->n; j++)
        {
            if (i == j)
            {
                G->edges[i][j] = 0;
            }
            else
            {
                G->edges[i][j] = LIMITLESS;
            }
        }
    }
    printf("請輸入 圖的頂點 邊和它的權值:\n");
    for (k = 0; k<G->e; k++)
    {
        scanf("%d%d%d", &start, &end, &w);
        G->edges[start][end] = w;
    }
}

void print(MGraph *G)
{
    int i, j;
    printf("頂點數:%d, 邊數:%d\n", G->n, G->e);
    printf("%d個頂點的資訊:\n", G->n);
    for (i = 0; i<G->n; i++)
    {
        printf("%5d%5d", G->vexs[i].no, G->vexs[i].info);
    }
    printf("\n各個頂點的連線情況:\n");
    printf("\t");
    for (i = 0; i<G->n; i++)
    {
        printf("[%d]\t", i);
    }
    printf("\n");
    for (i = 0; i<G->n; i++)
    {
        printf("[%d]\t", i);
        for (j = 0; j<G->n; j++)
        {
            if (G->edges[i][j] == LIMITLESS)
            {
                printf("oo\t");
            }
            else
            {
                printf("%d\t", G->edges[i][j]);
            }
        }
        printf("\n");
    }
}

void Ppath(int path[], int i, int v) //前向遞迴查詢路徑上的頂點
{
    int k;
    k = path[i];
    if (k == v)
    {
        return;
    }
    Ppath(path, k, v);
    printf("%d", k);
}

void Dispath(int dist[], int path[], int s[], int n, int v)
{
    int i;
    for (i = 0; i < n; i++)
    {
        if (s[i] == 1)
        {
            printf("從%d%d的最短路徑長度為:%d\t路徑為:", v, i, dist[i]);
            printf("%d", v);//輸出路徑上的起點
            Ppath(path, i, v); //輸出路徑上的中間點
            printf("%d\n", i);//輸出路徑上的終點
        }
        else
        {

            printf("從%d%d不存在路徑\n", v, i);
        }
    }
}

void Dijkstra(MGraph *g, int v)
{
    int mindis, i, j, u;
    int s[MAXV]; //表示這個頂點是否存入最短路線中
    int dist[MAXV];//表示起始點到此頂點的距離
    int path[MAXV];//表示此點的上一步是哪一個頂點
    for (i = 0; i < g->n; i++)
    {
        s[i] = 0;
        dist[i] = g->edges[v][i];
        if (g->edges[v][i] < LIMITLESS)
        {
            path[i] = v;
        }
        else
        {
            path[i] = -1;
        }
    }
    s[v] = 1;
    path[v] = 0;

    for (i = 0; i < g->n; i++)
    {
        mindis = LIMITLESS;//mindis置最小長度初值
        for (j = 0; j < g->n; j++) //選取不在s中且具有最小距離的頂點u
        {
            if (s[j] == 0 && dist[j] <mindis)
            {
                mindis = dist[j];
                u = j;
            }
        }
        s[u] = 1;
        for (j = 0; j < g->n; j++)
        {
            if (s[j] == 0)
            {
                if (g->edges[u][j] < LIMITLESS&&dist[u] + g->edges[u][j] < dist[j])
                {
                    dist[j] = dist[u] + g->edges[u][j];
                    path[j] = u;
                }
            }
        }
    }
    Dispath(dist, path, s, g->n, v);
}

int main(void)
{
    MGraph *g;
    g = malloc(sizeof(MGraph));
    creat(g);
    print(g);
    Dijkstra(g, 0);
    system("pause");
    return 0;
}

測試結果

這裡寫圖片描述
這裡寫圖片描述

參考圖紙:
這裡寫圖片描述