1、问题
最短路径问题(Dijkstra算法)用
优先队列实现,问题描述和分析和优先队列先看前面我的几篇博客
贪心算法之最短路径问题(Dijkstra算法)
2、用优点队列实现
#include <iostream>
#include <cstdio>
#include <stack>
#include <cstring>
#include <queue>
#include <cstdlib>
using namespace std;
//城市的节点数目的最大值
const int MAX_CITY_NUM = 100;
//节点权值的最大值
const int MAX_POLICY = 1e7;
/*
一定要记得如果初始化矩阵的话,肯定需要一个变量保存长和宽的最大值,
如果看到权重的话,肯定是需要有个变量保存最大值的权重
*/
struct Node
{
//value是节点值,然后min_dist是源点到这个节点的最短路径
int value, min_dist;
//注意这里前面不要加public
Node(int value, int min_dist)
{
this->value = value;
this->min_dist = min_dist;
}
//重载operator <
bool operator < (const Node &node) const
{
return this->value > node.value;
}
};
class Dijkstra
{
public:
//初始化工作
void init();
//dijkstra算法
void dijkstra();
//显示源点到其它顶点的经过的顶点
void showProcess();
//显示源点到各个顶点的最小权重
void showMinPolicy();
private:
//城市的节点数目和线段的个数和起始位置
int n, m, start;
//初始化权重矩阵
int map[MAX_CITY_NUM][MAX_CITY_NUM];
//源点到各个顶点的最短具体数组
int dist[MAX_CITY_NUM];
//下标表示当前节点值,然后值保存为上个节点值
int p[MAX_CITY_NUM];
//是否加入集合S,如果在集合S里面的话,值为true,否则在集合S-V里面,值为false;
bool flag[MAX_CITY_NUM];
};
//Dijkstra算法
void Dijkstra::dijkstra()
{
priority_queue<Node> queue;
Node node(start, 0);
queue.push(node);
//还是要记得初始化p[i], dist[i], flag[i]
memset(flag, false, sizeof(flag));
for (int i = 1; i <= n; ++i)
{
dist[i] = MAX_POLICY;
p[i] = -1;
}
dist[start] = 0;
// flag[start] = true;
while (!queue.empty())
{
//取出队列最小元素
Node node = queue.top();
queue.pop();
int value = node.value;
//这里防止重复
if (flag[value])
continue;
//为了防止重复必须设置为true,然后上面判断如果是true,就继续,
//相当于这里为打开了这把锁作标记,然后下次遇到这把锁的时候看是否做了标记
//如果做了标记,就continue;
flag[value] = true;
for (int i = 1; i <= n; ++i)
{
if (!flag[i] && map[value][i] < MAX_POLICY)
if (dist[i] > dist[value] + map[value][i])
{
dist[i] = dist[value] + map[value][i];
queue.push(Node(i, dist[i]));
p[i] = value;
}
}
}
}
//打印出每个顶点的路径,这里值保存了前一个节点的key
//所以我们需要用到栈的特点,先进后出
void Dijkstra::showProcess()
{
int value;
stack<int> stack;
for (int i = 1; i <= n; ++i)
{
value = p[i];
std::cout << "源点"<< start << "到"<< i << "的路径是";
while (value != -1)
{
stack.push(value);
value = p[value];
}
while (!stack.empty())
{
//pop函数是出来栈,没有返回值,先取出栈顶值,然后出栈
int node = stack.top();
stack.pop();
std::cout << node << "-";
}
std::cout << i << "最短距离为" << dist[i] << std::endl;
}
}
void Dijkstra::init()
{
//定点u到定点v的权重是w, 然后输入的起始地点是start;
int u, v, w;
std::cout << "请输入城市的节点个数" << std::endl;
std::cin >> n;
if (n <= 0)
{
std::cout << "输入的城市节点个数因该大于0" << std::endl;
return;
}
std::cout << "请输入城市之间线路的个数" << std::endl;
std::cin >> m;
if (m <= 0)
{
std::cout << "输入的城市之前的线路个数不能小于0" << std::endl;
return;
}
//邻接举证的初始化,默认都为最大值,注意这里下标都是从1开始
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
map[i][j] = MAX_POLICY;
}
}
std::cout << "请输入城市顶点到城市顶点之前的权重" << std::endl;
//这里也可以使用while(--m),因为不涉及到用i
for (int i = 0; i < m; ++i)
{
std::cin >> u >> v >> w;
if (u > n || v > n)
std::cout << "您输入的定点有误" << std::endl;
//如果2次输入一样顶点,那么取最小的
map[u][v] = min(map[u][v], w);
}
std::cout << "请输入小明的位置" << std::endl;
//请输入起始的顶点
std::cin >> start;
if (start < 0 || start > n)
{
std::cout << "输入的起始城市定点有误" << std::endl;
return;
}
}
void Dijkstra::showMinPolicy()
{
std::cout << "小明所在的位置 " << start << std::endl;
for (int i = 1; i <= n; ++i)
{
std::cout << "小明(" << start << ")要去的位置是" << i;
if (dist[i] == MAX_POLICY)
std::cout << "无路可到" << std::endl;
else
std::cout << "最短距离为" << dist[i] << std::endl;
}
}
int main()
{
Dijkstra dij;
dij.init();
dij.dijkstra();
dij.showMinPolicy();
dij.showProcess();
return 0;
}
3、运行结果和时间复杂度
请输入城市的节点个数
5
请输入城市之间线路的个数
7
请输入城市顶点到城市顶点之前的权重
1 2 2
1 3 3
2 3 5
2 4 6
3 4 7
3 5 1
4 5 4
请输入小明的位置
1
小明所在的位置 1
小明(1)要去的位置是1最短距离为0
小明(1)要去的位置是2最短距离为2
小明(1)要去的位置是3最短距离为3
小明(1)要去的位置是4最短距离为8
小明(1)要去的位置是5最短距离为4
源点1到1的路径是1最短距离为0
源点1到2的路径是1-2最短距离为2
源点1到3的路径是1-3最短距离为3
源点1到4的路径是1-2-4最短距离为8
源点1到5的路径是1-3-5最短距离为4
top()和push()函数时间复杂度是log(n),所以总的时间复杂度是 n*log(n) + n * n * long(n)
4、总结
1、优先队列可以每次取出集合里面的最大值和最小值,以后有这样的需求,我们要记得用优先队列。 2、如果是结构体使用优先队列,我们
需要在结构体里面进行运算符重载(operator <) 3、
比如优先队列里面多个重复数据,怎么避免重复数据弹出来呢?我们每次把弹出来数据进行判断,是否做了标记,如果是的话,就continue, 然后把弹出来得数据进行标记,比如设置成true.