基于优先队列的Dijkstra算法

前言

最短路径问题,即在给定的连接图中,求解节点之间的最短路径。Dijkstra算法是典型的单源最短路径算法,单源即只能求解某个节点到其他节点的最短路径。另外,此算法不能处理边权重为负的情况。

一、最短路径问题

最短路径问题是图论的一个经典算法问题,最短路径问题包含多种问题形式,本文着重讲解单源最短路径问题及其优化求解方法。如下图所示,给出由多个节点相互连接构成的图,节点之间连接的好坏用权重来衡量,我们要求解某点到任意点的最短路径,这是典型的单源最短路径问题,我们可以使用Dijkstra算法对其进行求解。
最短路径问题

二、Dijkstra算法

Dijkstra算法是典型的单源最短路径算法,其基本思想是以起点为中心,层层向外扩展,直到覆盖所有的节点,即找到了所有节点的最短路径。通常来说,我们需要准备两个集合S和U,S为已经找到最短路径的点的集合,U为尚未找到最短路径的点的集合.初始时源点,即出发点置于S中,源点的最短路径值始终为0,其余点则置于U中。从S中的点出发,遍历U中所有的点,每次找到一个点的最短路径,即从U中选择一个点加入S,直到U为空集。很明显,每次我们都需要从S中点出发,去查看U中的每个点并找到一个最小值,很明显算法的时间复杂度为O(n^2)。

三、优先队列

优先队列是队列的一种特殊形式,它满足队列的所有条件,区别于队列的是优先队列中的元素按照某种特定顺序排列,利用优先队列的这种特性,我们可以实现时间复杂度更低的Dijkstra算法。算法基本思想如下:
(1)初始化节点,赋予每个节点路径值为一个极大值,源点的路径值为0
(2)定义一个优先队列,队列中元素记录了节点的编号和节点的最短路径值,将源点压入队列。
(3)当队列非空,执行以下操作:
(a)u等于队顶的节点,w等于队顶节点的最短路径值
(b)遍历u的所有边,如果能找到节点v最短路径值小于v的当前值,更新v,将v压入队列。
(4)结束
使用优先队列求解的时间复杂度为O(nlogn)。

四、C++代码

使用C++实现上述算法,代码如下,编译环境为VS2019,最后得输出的结果是源点到各个节点的最短路径值。

#include<iostream>
#include<vector>
#include<queue>

using namespace std;

//节点信息类(包含节点和最短路径值)
class info
{
public:
	int node;//节点
	double weight;//权重
public:
	info(int node,double weight){
		this->node=node;
		this->weight=weight;
	}
	friend bool operator<(const info &info1 , const info &info2)
	{
		return info1.weight>info2.weight;  
	}
};

//图类
class cgraph{
public:
	int V;//顶点数量
	vector<info>*adj;//邻接矩阵(记录每个节点的所有边)
	double *dis;//最优距离
public:
	cgraph(int V);//构造函数(V为节点数)
	void add_edge(int nd1,int nd2,double weight);//添加边(节点1,节点2,边权重)
	void get_shortest_path(int src);//求解最短路径
};

cgraph::cgraph(int V)
{
		this->V=V;
		adj=new vector<info>[V];
		dis=new double[V];
		for(int i=0;i<V;i++)dis[i]=1e8;
	}

void cgraph::add_edge(int nd1,int nd2,double weight)
{
		adj[nd1].push_back(info(nd2,weight));
		adj[nd2].push_back(info(nd1,weight));
}

void cgraph::get_shortest_path(int src)
{
	priority_queue<info>current_node;//优先队列,记录节点编号和最短路径值,最短路径值小的优先
	dis[src]=0;//源点的最短路径值为0
	current_node.push(info(src,0));//源点压入队列

	//当队列非空继续寻找
	while(!current_node.empty()){
		int u=current_node.top().node;//u等于队顶节点
		double w=current_node.top().weight;//w等于对顶节点最短路径值
		current_node.pop();//弹出队顶节点
		//从对顶节点出发,更新节点的最短路径值
		for(int i=0;i<(int)adj[u].size();i++){
			int v=adj[u][i].node;
			if(w+adj[u][i].weight<dis[v]){
				dis[v]=w+adj[u][i].weight;
				current_node.push(info(v,dis[v]));
			}
		}
	}
	//输出dis追踪记录的最短路径值
	for(int i=0;i<V;i++)cout<<src<<"->"<<i<<"      dis="<<dis[i]<<endl;
}

int main()
{
	cgraph graph(9);//构造图

	//添加边
	graph.add_edge(0,1,4);
	graph.add_edge(0,7,8);
	graph.add_edge(1,2,8);
	graph.add_edge(1,7,11);
	graph.add_edge(2,3,7);
	graph.add_edge(2,5,4);
	graph.add_edge(2,8,2);
	graph.add_edge(3,4,9);
	graph.add_edge(3,5,14);
	graph.add_edge(4,5,10);
	graph.add_edge(5,6,2);
	graph.add_edge(6,7,1);
	graph.add_edge(6,8,6);
	graph.add_edge(7,8,7);

	//求解
	graph.get_shortest_path(0);

	cin.get();
}
  • 5
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值