贪心算法之用优先队列解决最短路径问题(Dijkstra算法)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011068702/article/details/79167000

1、问题

最短路径问题(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.
展开阅读全文

没有更多推荐了,返回首页