一、算法目的
- 使用邻接表实现Dijkstra算法
- 使用priority_queue(堆)优化取最小权值边的时间复杂度
- 从文件中读取的数据格式如下:
二、算法代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stdio.h>
#include <fstream>
#include <sstream>
#include <string>
#define maxnum 201 // 因为结点序号是从1开始的,因此设置为201,使得数组下标可以是1-200
#define edgenum 3734 // 总共有3734条边,可以由从文件读取数据的算法计算得到
#define INF 1000000
using namespace std;
typedef char VertexType;
//边结点结构体
typedef struct ArcNode
{
int adjvex;
int weight;
struct ArcNode* nextarc;
}ArcNode;
//顶点结构体
typedef struct VNode
{
VertexType data;
ArcNode* firstarc;
}VNode, AdjList[maxnum];
//邻接表
typedef struct
{
AdjList vertices;
int vexnum, arcnum;
}ALGraph;
//顶点节点,保存id和到源顶点的估算距离,优先队列需要的类型
struct Node
{
int id; //从源到目标顶点id
int w; //从源到id的距离
//因要实现最小堆,按升序排列,因而需要重载运算符,重定义优先级,使得结点升序
friend bool operator < (struct Node a, struct Node b)
{
return a.w > b.w;
}
};
int path[maxnum];
int visited[maxnum] = { 0 };
Node dist[maxnum];
priority_queue<Node>q;
void Dijkstra(ALGraph g, int v0, int n)
{
for (int i = 1; i <= n; i++)
{
dist[i].id = i; //从源v0到结点i
dist[i].w = INF; //初始距离为INF
path[i] = -1; //初始最短路径的前一个结点为-1
visited[i] = 0; //初始为所有结点都没有访问过
}
dist[v0].w = 0;
q.push(dist[v0]); //将源点入优先队列
while (!q.empty())
{
Node node = q.top(); //取得当前权值w最小值对应的结点
q.pop();
int u = node.id;
if (visited[u]) //若当前结点还没有访问过
continue;
visited[u] = 1;
ArcNode* p = g.vertices[u].firstarc; //取得当前结点对应的第一条边
while (p)
{
int tempv = p->adjvex;
int tempw = p->weight;
//若该边对应的另外一个结点还没有访问过并且源点到u加上u到tempv的权值小于源点直接到tempv的权值,更新权值
if (!visited[tempv] && dist[tempv].w > dist[u].w + tempw)
{
dist[tempv].w = dist[u].w + tempw;
path[tempv] = u; // 更新源点到tempv的最短路径的tempv前一结点为u
q.push(dist[tempv]);
}
p = p->nextarc;
}
}
}
void CreateALGraph(ALGraph& g, int arc, int vex)
{
ifstream fp;
int source;
int n, w;
fp.open("./data.txt", ios::in);
if (!fp.is_open()) {
cout << "打开文件失败!!" << endl;
return;
}
string line;
int count = 0;
g.arcnum = arc;
g.vexnum = vex;
int i;
for (i = 1; i <= vex; i++)
{
g.vertices[i].firstarc = NULL;
}
while (1)
{
fp >> source;
//if (source != 200)
// cout << source << endl;
if (!getline(fp, line)) break;
//cout << line;
stringstream ss(line); // 读取一行文件,将其当作输入流输入
string token;
while (ss >> token)
{
n = stoi(token);
if (ss >> token) {
w = stoi(token);
}
ArcNode* q = (ArcNode*)malloc(sizeof(ArcNode));
q->adjvex = n;
q->weight = w;
q->nextarc = g.vertices[source].firstarc;
g.vertices[source].firstarc = q;
//cout << n << "," << w << endl;
//count++; //计算边数
}
}
fp.close();
}
int main()
{
int m, n;
//顶点,边
n = 200;
m = edgenum;
//cin >> n >> m;
ALGraph g;
CreateALGraph(g, m, n);
//cout << g.vertices[1].firstarc->nextarc->adjvex <<g.vertices[1].firstarc->nextarc->weight << endl;
//cout << g.vertices[130].firstarc->nextarc->adjvex << g.vertices[130].firstarc->nextarc->weight << endl;
//cout << g.vertices[200].firstarc->adjvex << g.vertices[200].firstarc->weight << endl;
int temp;
int j;
while (1) {
cout << "请输入源点(输入-1结束程序):";
cin >> j;
if (j > 200 || j < 1) {
cout << "源点只能设置为1-200!" << endl;
continue;
}
if (j == -1)break;
cout << "******设置源点为 " << j << " ******" <<endl;
Dijkstra(g, j, n);
for (int i = 1; i <= n; i++ ) {
temp = i;
if (dist[i].w != INF) {
cout << "源点" << j << "到" << i <<"的最短路径为:" << dist[i].w << endl;
cout << "其路径为:" << temp;
while (path[temp] != -1) {
cout << " <- " << path[temp];
temp = path[temp];
}
cout << endl;
}
else
cout << "源点" << j << "到" << i << "没有路径" << endl;
cout << endl;
}
cout << "*************************" << endl << endl;
}
return 0;
}
三、运行效果
可以输入源点,计算不同源点到其余所有顶点的最短路径距离和经过的最短路径