您的位置:首页 > 其它

Dijkstra算法 --- 单源最短路

2015-12-13 22:10 387 查看
Dijkstra算法适用于边权值为正的情况,可用于计算正权图上的单元最短路。

其伪代码如下:

设d[v0] = 0, 其他d[i] = INF

循环n次{

  在所有未标号的结点中,选取d值最小的结点x

  给结点x加上永久标号

  对于从x出发的所有边,执行松弛操作。

}

//松弛操作的伪代码如下:

RELAX(u,v,w)

  if(u.d + w(u,v) < v.d){

    v.d = w.d + w(u,v);

    pre[v] = u;

  }

Dijkstra算法代码:

/* 改进的Dijkstra算法 ---  以边为存储结构 */
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;

const int maxn = 100;
const int INF = 65535;

/* 边结构 */
struct Edge{
int from, to, dist;//from,to,dist分别为边起点、终点和权值
Edge(int u, int v, int d) :from(u), to(v), dist(d){}
};

/* 此节点为优先队列的元素 相当于(i,d[i])二元组 */
struct HeapNode{
int d, u; //u记录最短路径,u为起点
bool operator<(const HeapNode& rhs) const{
return d > rhs.d; //小的优先
}
HeapNode(int a, int b) :d(a), u(b){}
};

/* 定义最短路算法结构体 由于边是用vector存储,且自动编号,似乎只适用于有向图 */
struct Dij{
int n, m;
vector<Edge> edges; //边集---保存边信息(自动编号为0~m-1)
vector<int> G[maxn]; //按点记录边 G[i]表示以i为起点的集合
//G[i][j]表示以i为起点的集合中其中一条边的在edges中的序号
bool visit[maxn];    //是否已经是永久标号
int d[maxn];        //s到各点的距离 s是起点 ---> 记录最短路
int pre[maxn];        //记录路径,pre[i]表示以i为终点的边的编号

/* n为顶点数 */
void init(int n){
this->n = n;
for (int i = 0; i < n; ++i){
G[i].clear(); //以i为起点的集合清空
}//for(i)
edges.clear(); //清空边集
}

void AddEdge(int from, int to, int dist){
edges.push_back(Edge(from, to, dist)); //加入边集
m = edges.size(); //m-1即为当前加入的边的序号
G[from].push_back(m - 1);
//无向图 加上反向边 有向图可省去下面三行
edges.push_back(Edge(to, from, dist));
m = edges.size();
G[to].push_back(m - 1);
}

void dijkstra(int s){
priority_queue<HeapNode> Q;
for (int i = 0; i < n; ++i){
d[i] = INF;
}//for(i)
d[s] = 0;
memset(visit, 0, sizeof visit);
Q.push(HeapNode(0,s)); //0,s分别为距离和起点
while (!Q.empty()){
HeapNode x = Q.top(); Q.pop(); //优先小地出栈,即先找到最小的d
int u = x.u;
//if (visit[u])
//    continue;
visit[u] = true;
for (int i = 0; i < G[u].size(); ++i){
Edge& e = edges[G[u][i]]; //取得以u为起点的边e
if (d[u] + e.dist < d[e.to]){
//经过点u和e到达e.to能更小
d[e.to] = d[u] + e.dist; //更新最小路径
pre[e.to] = G[u][i]; //记录路径
Q.push(HeapNode(d[e.to], e.to) );
}
}//for(i)
}
}
};

int main()
{
Dij dij;
int n, m; //点和边的数目
scanf("%d%d", &n, &m);
dij.init(n);
int a, b, c;
for (int i = 0; i < m; ++i){
scanf("%d%d%d", &a, &b, &c);
dij.AddEdge(a, b, c);
}
dij.dijkstra(0);
for (int i = 0; i < dij.n; ++i){
printf("%3d", dij.d[i]);
}
printf("\n");
//pre记录的是路径
int k = 8;
while (k){
printf("%3d", k);
k = dij.edges[dij.pre[k]].from; //取得前一个结点,
}
printf("%3d\n", k);
return 0;
}

/*
测试数据
9 16
0 1 1
0 2 5
1 2 3
1 3 7
1 4 5
2 4 1
2 5 7
3 4 2
3 6 3
4 5 3
4 6 6
4 7 9
5 7 5
6 7 2
6 8 7
7 8 4

结果: 数组d 0 1 4 7 5 8 10 12 16
0~8路径反序: 8 7 6 3 4 2 1 0
*/


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: