之前的讨论了无权图的最短路径算法。数据结构与算法——无权最短路径算法的C++实现
如果是加权图,那么问题就变得困难了,不过仍然可以采用无权情况的想法。
我们仍然保留之前的信息。因此,每个顶点会被标记为known或unknown,每个顶点保留一个尝试性的距离dv(这个距离是只使用一些known顶点作为中间顶点从s到v的最短路径的长),每个顶点还保留字段pv,该字段是记录引起dv变化的最后的顶点。
转载:http://blog.csdn.net/doufei_ccst/article/details/7841311
图顶点信息的数据结构:
-
- struct GraphNode{
- bool known;
- int dist;
- int path;
- };
Dijkstra算法简介:
解决单源最短路径问题的一般方法叫做Dijkstra算法。这个算法是贪心算法最好的例子。
贪心算法一般分阶段求解问题,在每个阶段它都把出现的东西当作是最好的去处理。
与无权最短路径算法一样,Dijkstra算法按阶段进行。在每个阶段,Dijkstra算法选择一个顶点v,它在所有的unknown顶点中具有最小的dv,同时算法声明从s到v的最短路径是known的。其它阶段由顶点dv的更新工作成。
主要思想就是根据已经确定了的点的距离,来确定该点相邻顶点的距离,不断的向外散射,直到所以的点的到起点的最短距离确定为止。
下面是Dijkstra算法具体步骤图示:
图1 用于测试的有向图
假设源点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算法的代码:
-
-
-
-
-
-
- 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(; ;){
-
- 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;
-
- 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 用于测试的有向图”的数据,该数据行12行,每行4列数据,第1列表示边序号、第2列和第3分别为相邻的起止顶点、第4列为边的权值):
- 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);
- };
-
-
-
-
-
-
-
-
- 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(; ;){
-
- 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;
-
- 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;
- }
- }
- }
-
- }
- }
-
-
-
-
-
-
-
- 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;
- }
- }
-
-
-
-
-
-
-
- void Graph::print()
- {
- cout << "******************************************************************" << endl;
-
- 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;
- }
-
-
-
-
-
-
-
-
- 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;
- }
-
-
-
-
-
-
-
-
-
-
- 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::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;
- }
-
- 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;
- }
-
-
-
-
-
-
-
-
-
- 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;
- }
-
-
-
-
-
-
-
-
- void release_buff(char ** const buff, const int valid_item_num)
- {
- for (int i = 0; i < valid_item_num; i++)
- free(buff[i]);
- }
运行结果: