1.這條路徑就是兩點之間的最短路徑 2.第一個頂點爲源點 3.最後一個頂點終點
從某固定源點觸發,求其到全部其餘頂點的最短路徑
求任意兩頂點間的最短路徑
能夠經過對每一個頂點使用一次單源(不是最好)
不考慮無向,無向咱們使用BFS,進行層次遍歷時,就能夠獲取
按照遞增(非遞減)的順序找出各個頂點的最短路徑
找出視圖源點v3到每一個頂點的最短路徑
從上圖路徑表咱們能夠看出,其路徑是按照BFS(有所不一樣),使用隊列進行遞增訪問各個頂點,從而遍歷了全部頂點。
注意:這裏咱們不使用棧來實現,由於棧用到回溯法,並且使用棧不能很好找到最短路徑長
void unWeight(MGraph G, int s) { int dist[MAXVEX]; //記錄達到下標對應頂點的最小距離 int path[MAXVEX]; //記錄每一個下標對應頂點的前一個通過的頂點 int i, v, w; //生成隊列一會使用 LinkQueue Q; InitQueue(&Q); for (i = 0; i < MAXVEX; i++) dist[i] = -1; //所有初始化爲-1,表示該頂點未被訪問過,沒有找到最短路徑到這個頂點 //將源點入隊 EnQueue(&Q, s); dist[s] = 0; path[s] = s; //將這裏設置爲他本身是本身的上一步,由於後面根本不會去設置他了 while (!EmptyQueue(Q)) { DeQueue(&Q, &v); for (w = 0; w < G.numVertexes; w++) { if (G.arc[v][w] == 1) //找到鄰接點w { if (dist[w] == -1) { dist[w] = dist[v] + 1; path[w] = v; EnQueue(&Q, w); } } } } for (i = 0; dist[i] != -1; i++) //對各個頂點的最短路徑長度進行打印,以及他的上一步路徑也打印 { printf("%d %c-%c\n", dist[i], G.vers[path[i]], G.vers[i]); } }
#pragma once #ifndef _QUEUE_H #define _QUEUE_H #include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 100 typedef int ElemType; typedef int Status; typedef struct _qNode { ElemType data; struct _qNode* next; }QNode,*QNodePtr; typedef struct { QNodePtr front,rear; //隊頭隊尾指針 }LinkQueue; Status InitQueue(LinkQueue* Q); Status EnQueue(LinkQueue* Q, ElemType e); Status DeQueue(LinkQueue* Q, ElemType* e); Status EmptyQueue(LinkQueue Q); Status getHead(LinkQueue Q,ElemType* e); #endif
#include "queue.h" Status InitQueue(LinkQueue* Q) { if (!Q) return ERROR; Q->front = Q->rear = (QNodePtr)malloc(sizeof(QNode)); if (!Q->front) return ERROR; Q->front->next = NULL; return OK; } Status EnQueue(LinkQueue* Q, ElemType e) { //尾插法 if (!Q) return ERROR; QNodePtr q = (QNodePtr)malloc(sizeof(QNode)); if (!q) return ERROR; q->data = e; q->next = (*Q).rear->next; (*Q).rear->next = q; Q->rear = q; return OK; } Status DeQueue(LinkQueue* Q, ElemType* e) { QNodePtr q; if (!Q || !e || EmptyQueue(*Q)) return ERROR; q = Q->front->next; Q->front->next = q->next; *e = q->data; if (Q->rear == q) Q->rear = Q->front; free(q); return OK; } Status EmptyQueue(LinkQueue Q) { if (!Q.front->next) return TRUE; return FALSE; } Status getHead(LinkQueue Q,ElemType* e) { QNodePtr q; if (EmptyQueue(Q)) return ERROR; q = Q.front->next; *e = q->data; return OK; }
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include "queue.h" #define MAXVEX 100 //最大頂點數 #define INFINITY 65535 //用0表示∞ typedef char VertexType; //頂點類型,字符型A,B,C,D... typedef int EdgeType; //邊上權值類型10,15,... //鄰接矩陣結構 typedef struct { VertexType vers[MAXVEX]; //頂點表 EdgeType arc[MAXVEX][MAXVEX]; //鄰接矩陣,可看做邊表 int numVertexes, numEdges; //圖中當前的頂點數和邊數 }MGraph; //建立鄰接矩陣 void CreateMGraph(MGraph* G); //顯示鄰接矩陣 void showGraph(MGraph G); //進行最小路徑獲取 void unWeight(MGraph G); int main() { MGraph MG; CreateMGraph(&MG); showGraph(MG); unWeight(MG,2); system("pause"); return 0; } //生成鄰接矩陣 void CreateMGraph(MGraph* G) { int i, j, k, w; G->numVertexes = 7; G->numEdges = 12; //讀入頂點信息 G->vers[0] = 'A'; G->vers[1] = 'B'; G->vers[2] = 'C'; G->vers[3] = 'D'; G->vers[4] = 'E'; G->vers[5] = 'F'; G->vers[6] = 'G'; G->vers[7] = 'H'; G->vers[8] = 'I'; //getchar(); //能夠獲取回車符 for (i = 0; i < G->numVertexes; i++) for (j = 0; j < G->numVertexes; j++) G->arc[i][j] = INFINITY; //鄰接矩陣初始化 //建立了有向鄰接矩陣 G->arc[0][1] = 1; G->arc[0][3] = 1; G->arc[1][3] = 1; G->arc[1][4] = 1; G->arc[2][0] = 1; G->arc[2][5] = 1; G->arc[3][2] = 1; G->arc[3][4] = 1; G->arc[3][5] = 1; G->arc[3][6] = 1; G->arc[4][6] = 1; G->arc[6][5] = 1; } //顯示鄰接矩陣 void showGraph(MGraph G) { for (int i = 0; i < G.numVertexes; i++) { for (int j = 0; j < G.numVertexes; j++) { if (G.arc[i][j] != INFINITY) printf("%5d", G.arc[i][j]); else printf(" 0"); } printf("\n"); } } void unWeight(MGraph G, int s) { int dist[MAXVEX]; //記錄達到下標對應頂點的最小距離 int path[MAXVEX]; //記錄每一個下標對應頂點的前一個通過的頂點 int i, v, w; //生成隊列一會使用 LinkQueue Q; InitQueue(&Q); for (i = 0; i < MAXVEX; i++) dist[i] = -1; //所有初始化爲-1,表示該頂點未被訪問過,沒有找到最短路徑到這個頂點 //將源點入隊 EnQueue(&Q, s); dist[s] = 0; path[s] = s; //將這裏設置爲他本身是本身的上一步,由於後面根本不會去設置他了 while (!EmptyQueue(Q)) { DeQueue(&Q, &v); for (w = 0; w < G.numVertexes; w++) { if (G.arc[v][w] == 1) //找到鄰接點w { if (dist[w] == -1) { dist[w] = dist[v] + 1; path[w] = v; EnQueue(&Q, w); } } } } for (i = 0; dist[i] != -1; i++) { printf("%d %c-%c\n", dist[i], G.vers[path[i]], G.vers[i]); } }
從v1-v6最小爲6,即v1-v4-v7-v6。不必定爲通過頂點最小的路,和上面的無權最短路徑不一樣
會致使一直循環,獲取無窮收益。致使全部算法都失效
方法和上面的無權路徑仍是類似的,就是按照遞增的順序找出各個頂點的最短路
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include "queue.h" #define MAXVEX 100 //最大頂點數 #define INFINITY 65535 //用0表示∞ typedef char VertexType; //頂點類型,字符型A,B,C,D... typedef int EdgeType; //邊上權值類型10,15,... //鄰接矩陣結構 typedef struct { VertexType vers[MAXVEX]; //頂點表 EdgeType arc[MAXVEX][MAXVEX]; //鄰接矩陣,可看做邊表 int numVertexes, numEdges; //圖中當前的頂點數和邊數 }MGraph; //建立鄰接矩陣 void CreateMGraph(MGraph* G); //顯示鄰接矩陣 void showGraph(MGraph G); //迪卡斯特拉算法,獲取最短路徑 void Dijkatra(MGraph G, int s); void Dijkatra(MGraph G,int s) { int path[MAXVEX]; //是數組下標表示的頂點所經歷的前一個頂點 int dist[MAXVEX]; //是數組下標表示的頂點的最小權值路徑和 //上面兩個數組都有做用,和無權最短路徑一致,可是無權最短路徑能夠使用dist是否被設置來判斷一個頂點是否被訪問, //可是這裏沒法使用,由於dist和普里姆算法中的lowcost同樣,是使用貪心算法時,每到一個頂點,咱們都會所有更新dist //因此咱們須要另一個數組來標誌各個頂點是否被訪問 int final[MAXVEX]; int i,j,k,min; //對數據進行初始化 for (i = 0; i < G.numVertexes;i++) { final[i] = 0; //0表示該數組下標所表示的頂點未被訪問 path[i] = 0; //初始化路徑數組爲0,表示當前每一個都是獨立的根節點 dist[i] = G.arc[s][i]; //這一步是重點:初始化路徑數組的值爲起始v0到各個點的權值 } dist[s] = 0; //到源點本身的路徑爲0 path[s] = s; //設置源點的前一個頂點就是本身 final[s] = 1; //源點被訪問過了 //開始主循環,每次求的v0(s)到某個v頂點的最短路徑 for (i = 0; i < G.numVertexes;i++) { min = INFINITY; //和普里姆算法類似 for (j = 0; j < G.numVertexes;j++) //因爲是有向圖因此都要從0開始,找到他的每一個鄰接點 { if (!final[j]&&dist[j]<min) //如果該頂點沒有被訪問過,且該點到s點的距離小於min,咱們就將min設置爲他 { k = j; //記錄下該v到s點的下標和min最小路徑 min = dist[j]; } } final[k] = 1; //將目前找到的距離v0(S)最近的頂點置爲1 for (j = 0; j < G.numVertexes;j++) //修正當前最短路徑即距離 { //修正方法就是循環k的每一個鄰接點,咱們做爲三角形來看,如果兩邊之和小於第三邊,那咱們原來找的那條直接的最短邊就失效了,用這兩條直接代替 //因此咱們將距離修改,路徑設置爲他的上一步k, if (!final[j]&&(min+G.arc[k][j])<dist[j]) { //說明找到了更短的路徑,修改dist和path數組 dist[j] = min + G.arc[k][j]; //修改當前路徑長度 path[j] = k; } } } for (i = 0; i<G.numVertexes; i++) { printf("%d %c-%c\n", dist[i], G.vers[path[i]], G.vers[i]); } } int main() { MGraph MG; CreateMGraph(&MG); showGraph(MG); Dijkatra(MG,0); system("pause"); return 0; } //生成鄰接矩陣 void CreateMGraph(MGraph* G) { int i, j, k, w; G->numVertexes = 7; G->numEdges = 12; //讀入頂點信息 G->vers[0] = 'A'; G->vers[1] = 'B'; G->vers[2] = 'C'; G->vers[3] = 'D'; G->vers[4] = 'E'; G->vers[5] = 'F'; G->vers[6] = 'G'; G->vers[7] = 'H'; G->vers[8] = 'I'; //getchar(); //能夠獲取回車符 for (i = 0; i < G->numVertexes; i++) for (j = 0; j < G->numVertexes; j++) G->arc[i][j] = INFINITY; //鄰接矩陣初始化 //建立了有向鄰接矩陣 G->arc[0][1] = 2; G->arc[0][3] = 1; G->arc[1][3] = 3; G->arc[1][4] = 10; G->arc[2][0] = 4; G->arc[2][5] = 5; G->arc[3][2] = 2; G->arc[3][4] = 2; G->arc[3][5] = 8; G->arc[3][6] = 4; G->arc[4][6] = 6; G->arc[6][5] = 1; } //顯示鄰接矩陣 void showGraph(MGraph G) { for (int i = 0; i < G.numVertexes; i++) { for (int j = 0; j < G.numVertexes; j++) { if (G.arc[i][j] != INFINITY) printf("%5d", G.arc[i][j]); else printf(" 0"); } printf("\n"); } }
void Dijkatra(MGraph G,int s) { int path[MAXVEX]; //是數組下標表示的頂點所經歷的前一個頂點 int dist[MAXVEX]; //是數組下標表示的頂點的最小權值路徑和 //上面兩個數組都有做用,和無權最短路徑一致,可是無權最短路徑能夠使用dist是否被設置來判斷一個頂點是否被訪問, //可是這裏沒法使用,由於dist和普里姆算法中的lowcost同樣,是使用貪心算法時,每到一個頂點,咱們都會所有更新dist //因此咱們須要另一個數組來標誌各個頂點是否被訪問 int final[MAXVEX]; int i,j,k,min; //對數據進行初始化 for (i = 0; i < G.numVertexes;i++) { final[i] = 0; //0表示該數組下標所表示的頂點未被訪問 path[i] = 0; //初始化路徑數組爲0,表示當前每一個都是獨立的根節點 dist[i] = G.arc[s][i]; //這一步是重點:初始化路徑數組的值爲起始v0到各個點的權值 } dist[s] = 0; //到源點本身的路徑爲0 path[s] = s; //設置源點的前一個頂點就是本身 final[s] = 1; //源點被訪問過了 //開始主循環,每次求的v0(s)到某個v頂點的最短路徑 for (i = 0; i < G.numVertexes;i++) { min = INFINITY; //和普里姆算法類似 for (j = 0; j < G.numVertexes;j++) //因爲是有向圖因此都要從0開始,找到他的每一個鄰接點 { if (!final[j]&&dist[j]<min) //如果該頂點沒有被訪問過,且該點到s點的距離小於min,咱們就將min設置爲他 { k = j; //記錄下該v到s點的下標和min最小路徑 min = dist[j]; } } final[k] = 1; //將目前找到的距離v0(S)最近的頂點置爲1 for (j = 0; j < G.numVertexes;j++) //修正當前最短路徑即距離 { //修正方法就是循環k的每一個鄰接點,咱們做爲三角形來看,如果兩邊之和小於第三邊,那咱們原來找的那條直接的最短邊就失效了,用這兩條直接代替 //因此咱們將距離修改,路徑設置爲他的上一步k, if (!final[j]&&(min+G.arc[k][j])<dist[j]) { //說明找到了更短的路徑,修改dist和path數組 dist[j] = min + G.arc[k][j]; //修改當前路徑長度 path[j] = k; } } } for (i = 0; i<G.numVertexes; i++) { printf("%d %c-%c\n", dist[i], G.vers[path[i]], G.vers[i]); } }
for (j = 0; j < G.numVertexes;j++) //修正當前最短路徑即距離 { //修正方法就是循環k的每一個鄰接點,咱們做爲三角形來看,如果兩邊之和小於第三邊,那咱們原來找的那條直接的最短邊就失效了,用這兩條直接代替 //因此咱們將距離修改,路徑設置爲他的上一步k, if (!final[j]&&(min+G.arc[k][j])<dist[j]) { //說明找到了更短的路徑,修改dist和path數組 dist[j] = min + G.arc[k][j]; //修改當前路徑長度 path[j] = k; } }
例如上圖:咱們肯定了v0點的最短距離是v0-v3是1,因此咱們將final[3]=1
由於v1未被標記,並且min(就是d(v0-v3))+d(v3-v1)=1+3大於原來的dist[1]=2,因此不予理會
由於v2未被標記,並且min(就是d(v0-v3))+d(v3-v2)=1+2小於原來的dist[2]=4,因此咱們將他的距離修改,變爲dist[2]=min+E(3,2),將他的路徑也作修正,他的是一個頂點變爲v3,path[2]=3
for (j = 0; j < G.numVertexes;j++) //修正當前最短路徑即距離 { //修正方法就是循環k的每一個鄰接點,咱們做爲三角形來看,如果兩邊之和小於第三邊,那咱們原來找的那條直接的最短邊就失效了,用這兩條直接代替 //因此咱們將距離修改,路徑設置爲他的上一步k, if (!final[j]&&(min+G.arc[k][j])<dist[j]) { //說明找到了更短的路徑,修改dist和path數組 dist[j] = min + G.arc[k][j]; //修改當前路徑長度 path[j] = k; } }
有向圖和無向圖無非就是矩陣不對稱而已,求最短路徑幾乎是一致的。因此沒必要考慮太多
Dijkstra算法解決了從某個頂點到其他各頂點的最短路徑。其時間複雜度是O(n*2)