資料結構與演算法——最短路徑Dijkstra演算法的C++實現
之前的討論了無權圖的最短路徑演算法。資料結構與演算法——無權最短路徑演算法的C++實現
如果是加權圖,那麼問題就變得困難了,不過仍然可以採用無權情況的想法。
我們仍然保留之前的資訊。因此,每個頂點會被標記為known或unknown,每個頂點保留一個嘗試性的距離dv(這個距離是隻使用一些known頂點作為中間頂點從s到v的最短路徑的長),每個頂點還保留欄位pv,該欄位是記錄引起dv變化的最後的頂點。
圖頂點資訊的資料結構:
//儲存每個頂點資訊的資料結構
struct GraphNode{
bool known;//當前頂點距離起點的距離是否確定
int dist;//當前頂點到起點的最短距離
int path;//當前頂點距離起點的最短路徑的前一個頂點
};
Dijkstra演算法簡介:
解決單源最短路徑問題的一般方法叫做Dijkstra演算法。這個演算法是貪心演算法最好的例子。
貪心演算法一般分階段求解問題,在每個階段它都把出現的東西當作是最好的去處理。
與無權最短路徑演算法一樣,Dijkstra演算法按階段進行。在每個階段,Dijkstra演算法選擇一個頂點v,它在所有的unknown頂點中具有最小的dv,同時演算法宣告從s到v的最短路徑是known的。其它階段由頂點dv的更新工作成。
主要思想就是根據已經確定了的點的距離,來確定該點相鄰頂點的距離,不斷的向外散射,直到所以的點的到起點的最短距離確定為止。
下面是Dijkstra演算法具體步驟圖示:
假設源點s=v1,下面是求s到其它點的最短距離。
第一步:初始化頂點資訊
第二步:
頂點v1就是頂點s,那麼距離頂點v1路徑長為0,將頂點v1欄位的known設為true。
與s相鄰的點為v2,v4。v1到v2的距離為4,v1到v4的距離為1。並更新v2和v4的距離。
p2 = v1; p4 = v1;
第三步:
尋找unknown的頂點中距離最短的頂點,並將該頂點標記為known。所以將v4標記為known。
然後,再根據頂點v4的dv值更新與v4相鄰的所有點(v3,v6,v7,v5)的距離。
d4+d43 = 1+2 < d3,所以d3 = 3;p3 = v4;
d4+d46 = 1+8 < d6,所以d6 = 9;p6 = v4;
d4+d47 = 1+4 < d7,所以d7 = 5;p7 = v4;
d4+d45 = 1+2 < d5,所以d5 = 3;p5 = v4;
第四步:
執行第三步,尋找unknown的頂點中距離最短的頂點,並將該頂點標記為known。所以將v2標記為known。
然後,再根據頂點v2的dv值更新與v2相鄰的所有點(v4,v5)的距離。因為v4已經確定了,就不用更新了。因為d2+d25 = 2 + 10 > d5,d5的值為3。所以不需要更新d5。
第五步:
執行第三步,尋找unknown的頂點中距離最短的頂點,並將該頂點標記為known。所以將v5標記為known。
然後,再根據頂點v5的dv值更新與v5相鄰的所有點(v7)的距離。因為d5+d57 = 3+6>d7,所以d7不用更新。
第六步:
執行第三步,尋找unknown的頂點中距離最短的頂點,並將該頂點標記為known。所以將v3標記為known。
然後,再根據頂點v3的dv值更新與v3相鄰的所有點(v1,v6)的距離。因為v1是known的,所以d1不用更新。d3+d36 = 3+5 < d6,所以d6 = d3+d36 = 8; p6 = v3;
第七步:
執行第三步,尋找unknown的頂點中距離最短的頂點,並將該頂點標記為known。所以將v7標記為known。
然後,再根據頂點v7的dv值更新與v7相鄰的所有點(v6)的距離。因為d7+d76 = 5+1<d6,所以d6=d7+d76 = 6; p6 = v7;
第八步:
執行第三步,尋找unknown的頂點中距離最短的頂點,並將該頂點標記為known。所以將v6標記為known。
然後,再根據頂點v6的dv值更新與v6相鄰的所有點的距離。結果沒有v6指向的其它頂點。
第九步:
執行第三步,尋找unknown的頂點中距離最短的頂點,並將該頂點標記為known。結果沒有找到滿足條件的頂點,表明此時已經確定了所有頂點的距離,則退出演算法。
Dijkstra演算法的基本步驟:
1、初始化頂點資訊;v.known = flase;
v.dist = INFINITY;
v.path = 0;
2、對起點s的dist欄位設為0;s.dist = 0;
3、從所有頂點中找到dist最小的並且known為false的頂點v。然後將該頂點v的known置為true;
然後更新與頂點v相鄰的所有其它known為false的頂點w的dist和path的值。
如果v.dist+distance(v,w) < w.dist;則更新w.dist = v.dist + distance(v, w);w.path=v;
4、迴圈執行第3步,直到從所有頂點中找不到known為false的頂點v為止,找不到合適的頂點的時候則退出演算法。
Dijkstra演算法的虛擬碼:
void Graph::dijkstra(Vertex s)
{
//初始化頂點資訊
for each Vertex v
{
v.known = false;
v.dist = INFINITY;
v.path = 0;
}
//起點s的dist設為0
s.dist = 0;
//迴圈執行第3步
for(; ;)
{
//從所有頂點中找到dist最小的並且known為false的頂點v
Vertex v = unknown smallest distance vertex;
//如果沒有找到滿足條件的v,則退出演算法(此時所有頂點已經全部確定了)
if(v == NOT_A_VERTEX)
break;
//將該頂點v的known置為true
v.known = true;
//更新與頂點v相鄰的所有其它known為false的頂點w的dist和path的值
for each Vertex w adjacent to v
{
if(!w.known)
{
//更新w.dist
if(v.dist+distance(v,w) < w.dist)
{
w.dist = v.dist + distance(v,w);
w.path = v;
}
}
}
}
}
Dijkstra演算法的程式碼:
/*************************************************
* 函式名稱:dijkstra(int src)
* 功能描述:求無權圖的任意點到其它頂點的距離
* 引數列表:src是起點
* 返回結果:void
*************************************************/
void Graph::dijkstra(int src)
{
//初始化頂點資訊
for(int i = 0; i < vertex_num; ++i){
nodeArr[i].known = false;
nodeArr[i].dist = INFINITY;
nodeArr[i].path = 0;
}
//重要的一步,開啟演算法的關鍵一步
nodeArr[src].dist = 0;
for(; ;){
//找到unknown的dist最小的頂點
int v = 0;
int max = INFINITY;
for(int i = 0; i < vertex_num; ++i){
if(!nodeArr[i].known && (max > nodeArr[i].dist)){
max = nodeArr[i].dist;
v = i;
}
}
//沒有找到滿足條件的頂點,退出演算法
if(max == INFINITY)
break;
nodeArr[v].known = true;
//更新與v相鄰所有頂點w的dist,path
for(list<Node>::iterator it = graph_list[v].begin(); it != graph_list[v].end(); ++it){
if(!nodeArr[(*it).vertex].known){
if(nodeArr[v].dist + (*it).weight < nodeArr[(*it).vertex].dist){
nodeArr[(*it).vertex].dist = nodeArr[v].dist + (*it).weight;
nodeArr[(*it).vertex].path = v;
}
}
}
}
}
圖類的介面:
/*******************************************************
* 類名稱: 鄰接表圖
********************************************************/
class Graph{
private:
int edge_num;//圖邊的個數
int vertex_num;//圖的頂點數目
list<Node> * graph_list;//鄰接表
vector<GraphNode> nodeArr;//儲存每個頂點資訊的陣列
public:
Graph(){}
Graph(char* graph[], int edgenum);
~Graph();
void print();
void dijkstra(int src);
void printShorestPath();
private:
vector<int> get_graph_value(char* graph[], int columns);
void addEdge(char* graph[], int columns);
};
測試主函式:
int main(int argc, char *argv[])
{
char *topo[5000];
int edge_num;
char *demand;
int demand_num;
char *topo_file = argv[1];
edge_num = read_file(topo, 5000, topo_file);
if (edge_num == 0)
{
printf("Please input valid topo file.\n");
return -1;
}
int src;
cout << "輸入求最短路徑的起點:";
cin >> src;
Graph G(topo, edge_num);
G.print();
cout << "Dijkstra: " << endl;
G.dijkstra(src);
G.printShorestPath();
release_buff(topo, edge_num);
return 0;
}
測試的圖的資料:
1,1,2,2
2,1,4,1
3,2,4,3
4,2,5,10
5,3,1,4
6,3,6,5
7,4,3,2
8,4,6,8
9,4,5,2
10,4,7,4
11,5,7,6
12,7,6,1
圖類的原始碼:
#ifndef GRAPH_H
#define GRAPH_H
#include <list>
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iterator>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <queue>
using namespace std;
#define MAX_VERTEX_NUM 600
#define INFINITY 1000000//將INFINITY定義為無窮大的值
//儲存每個頂點資訊的資料結構
struct GraphNode{
bool known;//當前頂點距離起點的距離是否確定
int dist;//當前頂點到起點的最短距離
int path;//當前頂點距離起點的最短路徑的前一個頂點
};
//圖節點資訊
typedef struct Node{
int edge_num;//邊號
int src;//源點
int vertex;//自身
int weight;//邊的權重
}Node;
/*******************************************************
* 類名稱: 鄰接表圖
********************************************************/
class Graph{
private:
int edge_num;//圖邊的個數
int vertex_num;//圖的頂點數目
list<Node> * graph_list;//鄰接表
vector<GraphNode> nodeArr;//儲存每個頂點資訊的陣列
public:
Graph(){}
Graph(char* graph[], int edgenum);
~Graph();
void print();
void dijkstra(int src);
void printShorestPath();
private:
vector<int> get_graph_value(char* graph[], int columns);
void addEdge(char* graph[], int columns);
};
/*************************************************
* 函式名稱:dijkstra(int src)
* 功能描述:求無權圖的任意點到其它頂點的距離
* 引數列表:src是起點
* 返回結果:void
*************************************************/
void Graph::dijkstra(int src)
{
//初始化頂點資訊
for(int i = 0; i < vertex_num; ++i){
nodeArr[i].known = false;
nodeArr[i].dist = INFINITY;
nodeArr[i].path = 0;
}
//重要的一步,開啟演算法的關鍵一步
nodeArr[src].dist = 0;
for(; ;){
//找到unknown的dist最小的頂點
int v = 0;
int max = INFINITY;
for(int i = 0; i < vertex_num; ++i){
if(!nodeArr[i].known && (max > nodeArr[i].dist)){
max = nodeArr[i].dist;
v = i;
}
}
//沒有找到滿足條件的頂點,退出演算法
if(max == INFINITY)
break;
nodeArr[v].known = true;
//更新與v相鄰所有頂點w的dist,path
for(list<Node>::iterator it = graph_list[v].begin(); it != graph_list[v].end(); ++it){
if(!nodeArr[(*it).vertex].known){
if(nodeArr[v].dist + (*it).weight < nodeArr[(*it).vertex].dist){
nodeArr[(*it).vertex].dist = nodeArr[v].dist + (*it).weight;
nodeArr[(*it).vertex].path = v;
}
}
}
}
}
/*************************************************
* 函式名稱:printShorestPath()
* 功能描述:將獲得的src頂點到其它頂點的最短路徑輸出
* 引數列表:無
* 返回結果:無
*************************************************/
void Graph::printShorestPath()
{
cout << "頂點\t" << "known\t" << "dist\t" << "path" << endl;
for(int i = 0; i < vertex_num; ++i){
if(nodeArr[i].known)
cout << i << "\t" << nodeArr[i].known << "\t" << nodeArr[i].dist << "\t" << nodeArr[i].path << endl;
}
}
/*************************************************
* 函式名稱:print
* 功能描述:將圖的資訊以鄰接表的形式輸出到標準輸出
* 引數列表:無
* 返回結果:無
*************************************************/
void Graph::print()
{
cout << "******************************************************************" << endl;
//for(int i = 0 ; i < MAX_VERTEX_NUM; ++i){
for(int i = 0 ; i < vertex_num; ++i){
if(graph_list[i].begin() != graph_list[i].end()){
cout << i << "-->";
for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){
cout << (*it).vertex << "(邊號:" << (*it).edge_num << ",權重:" << (*it).weight << ")-->";
}
cout << "NULL" << endl;
}
}
cout << "******************************************************************" << endl;
}
/*************************************************
* 函式名稱:get_graph_value
* 功能描述:將圖的每一條邊的資訊儲存到一個陣列中
* 引數列表: graph:指向圖資訊的二維陣列
columns:圖的第幾條邊
* 返回結果:無
*************************************************/
vector<int> Graph::get_graph_value(char* graph[], int columns)
{
vector<int> v;
char buff[20];
int i = 0, j = 0, val;
memset(buff, 0, 20);
while((graph[columns][i] != '\n') && (graph[columns][i] != '\0')){
if(graph[columns][i] != ','){
buff[j] = graph[columns][i];
j++;
}
else{
j = 0;
val = atoi(buff);
v.push_back(val);
memset(buff, 0, 20);
}
i++;
}
val = atoi(buff);
v.push_back(val);
return v;
}
/*************************************************
* 函式名稱:addEdge
* 功能描述:將圖的每一條邊的資訊加入圖的鄰接表中
* 引數列表:graph:指向圖資訊的二維陣列
columns:圖的第幾條邊
* 返回結果:無
*************************************************/
void Graph::addEdge(char* graph[], int columns)
{
Node node;
vector<int> v = get_graph_value(graph, columns);
node.edge_num = v[0];
node.src = v[1];
node.vertex = v[2];
node.weight = v[3];
//根據頂點的標號,求的總的頂點數目
if(node.vertex > vertex_num)
vertex_num = node.vertex;
//要考慮重複的邊,但是邊的權重不一樣
for(list<Node>::iterator it = graph_list[node.src].begin(); it != graph_list[node.src].end(); ++it){
if((*it).vertex == node.vertex){
if((*it).weight > node.weight){
(*it).weight = node.weight;
}
return;
}
}
graph_list[node.src].push_back(node);
}
/*************************************************
* 函式名稱:建構函式
* 功能描述:以鄰接表的形式儲存圖的資訊,並儲存必須經過的頂點
* 引數列表:graph:指向圖資訊的二維陣列
edgenum:圖的邊的個數
* 返回結果:無
*************************************************/
Graph::Graph(char* graph[], int edgenum):nodeArr(MAX_VERTEX_NUM)
{
edge_num = edgenum;
vertex_num = 0;
graph_list = new list<Node>[MAX_VERTEX_NUM+1];
for(int i = 0; i < edgenum; ++i){
addEdge(graph, i);
}
//對頂點資訊進行初始化
for(int i = 0; i < MAX_VERTEX_NUM; ++i){
nodeArr[i].known = false;
nodeArr[i].dist = INFINITY;
nodeArr[i].path = -1;//如果為-1,表示沒有該點,配合dijkstra演算法使用
}
vertex_num++;
}
/*************************************************
* 函式名稱:解構函式
* 功能描述:釋放動態分配的記憶體
* 引數列表:無
* 返回結果:無
*************************************************/
Graph::~Graph()
{
delete[] graph_list;
}
#endif
測試函式的原始碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <sys/timeb.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include "graphDijkstra.h"
#define MAX_LINE_LEN 4000
int read_file(char ** const buff, const unsigned int spec, const char * const filename);
void release_buff(char ** const buff, const int valid_item_num);
int main(int argc, char *argv[])
{
char *topo[5000];
int edge_num;
char *demand;
int demand_num;
char *topo_file = argv[1];
edge_num = read_file(topo, 5000, topo_file);
if (edge_num == 0)
{
printf("Please input valid topo file.\n");
return -1;
}
int src;
cout << "輸入求最短路徑的起點:";
cin >> src;
Graph G(topo, edge_num);
G.print();
cout << "Dijkstra: " << endl;
G.dijkstra(src);
G.printShorestPath();
release_buff(topo, edge_num);
return 0;
}
/****************************************************************
* 函式名稱:read_file
* 功能描述: 讀取檔案中的圖的資料資訊
* 引數列表: buff是將檔案讀取的圖資訊儲存到buff指向的二維陣列中
* spec是檔案中圖最大允許的邊的個數
* filename是要開啟的圖檔案
* 返回結果:無
*****************************************************************/
int read_file(char ** const buff, const unsigned int spec, const char * const filename)
{
FILE *fp = fopen(filename, "r");
if (fp == NULL)
{
printf("Fail to open file %s, %s.\n", filename, strerror(errno));
return 0;
}
printf("Open file %s OK.\n", filename);
char line[MAX_LINE_LEN + 2];
unsigned int cnt = 0;
while ((cnt < spec) && !feof(fp))
{
line[0] = 0;
fgets(line, MAX_LINE_LEN + 2, fp);
if (line[0] == 0) continue;
buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2);
strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1);
buff[cnt][4001] = 0;
cnt++;
}
fclose(fp);
printf("There are %d lines in file %s.\n", cnt, filename);
return cnt;
}
/****************************************************************
* 函式名稱:release_buff
* 功能描述: 釋放剛才讀取的檔案中的圖的資料資訊
* 引數列表: buff是指向檔案讀取的圖資訊
* valid_item_num是指圖中邊的個數
* 返回結果:void
*****************************************************************/
void release_buff(char ** const buff, const int valid_item_num)
{
for (int i = 0; i < valid_item_num; i++)
free(buff[i]);
}
執行結果:
相關文章
- 資料結構與演算法——無權最短路徑演算法的C++實現資料結構演算法C++
- 演算法與資料結構之-圖的最短路徑演算法資料結構
- 最短路徑之Dijkstra演算法演算法
- python實現Dijkstra演算法之 最短路徑問題Python演算法
- 最短路徑問題 (dijkstra演算法)演算法
- 單源最短路徑-Dijkstra演算法演算法
- 單源最短路徑 -- Dijkstra演算法演算法
- 最短路徑——Dijkstra演算法和Floyd演算法演算法
- 最短路徑—Dijkstra演算法和Floyd演算法演算法
- 圖的單源最短路徑(Dijkstra演算法)演算法
- 10行實現最短路演算法——Dijkstra演算法
- 資料結構與演算法——B樹的C++實現資料結構演算法C++
- 資料結構------最短路徑Dijkstra和最小生成樹Prim資料結構
- 資料結構與演算法——AVL樹類的C++實現資料結構演算法C++
- 資料結構與演算法——普通樹的定義與C++實現資料結構演算法C++
- 最短路dijkstra演算法演算法
- 最短路 - Dijkstra 演算法演算法
- 資料結構和演算法學習筆記九:最短路徑資料結構演算法筆記
- 資料結構 最短路徑之—迪傑斯特拉演算法資料結構演算法
- 最短路徑——dijkstra演算法程式碼(c語言)演算法C語言
- 最短路徑—Dijkstra(迪傑斯特拉)演算法演算法
- 0016:單源最短路徑(dijkstra演算法)演算法
- 求兩點之間最短路徑-Dijkstra演算法演算法
- 資料結構與演算法——不相交集類的C++實現資料結構演算法C++
- 資料結構與演算法——迪傑斯特拉(Dijkstra)演算法資料結構演算法
- 如何在 Java 中實現 Dijkstra 最短路演算法Java演算法
- 單源最短路徑複習--Dijkstra演算法和Floyd演算法演算法
- 最短路徑演算法總結演算法
- 路徑規劃演算法 - 求解最短路徑 - Dijkstra(迪傑斯特拉)演算法演算法
- 最短路徑--dijkstra演算法、弗洛伊德(Floyd)演算法(帶路徑輸出)演算法
- HDU3790 最短路徑問題【Dijkstra演算法】演算法
- 最短路演算法之:Dijkstra 演算法演算法
- 《圖論》——最短路徑 Dijkstra演算法(戴克斯特拉演算法)圖論演算法
- [最短路徑問題]Dijkstra演算法(含還原具體路徑)演算法
- 資料結構學習(C++)——圖【4】(最短路徑) (轉)資料結構C++
- 一篇文章講透Dijkstra最短路徑演算法演算法
- SQL Server與最短路徑演算法SQLServer演算法
- 演算法與資料結構-棧(Stack)-Java實現演算法資料結構Java