圖之從一個頂點到其餘各個頂點的最短路徑(有向圖)
目錄
- 從一個頂點到其餘各個頂點最短路徑的簡介
- 舉例以及詳細分析
- 程式碼塊
- 測試結果
從一個頂點到其餘各個頂點最短路徑的簡介(又名單元最短路徑)
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;
}
測試結果
參考圖紙: