最小堆优化dijkstra算法(小白也能看懂)

在原版dijkstra算法中每轮新增纳入点,通过枚举中转点修改dist数组后,还要遍历一次dist数组找到下一个最近点,这个过程十分耗费时间,而使用优先队列可以提高效率

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int N=10005;
typedef struct Edge{//边的终点编号(边不需要单独编号)和权值
	int to,w;//那没有起点怎么开始呢?字典树中是构造了个root节点,而这里起点就是源点
}*adj;//结构体指针(存指针效率高)后面使用adj时候等效于Edge*
struct cmp{   //自定义优先级,返回true表示前者优先级更低(与排序刚好相反) 
	bool operator()(const adj a,const adj b){
		return a->w > b->w;
	}//因为了用的是结构体指针,所以要麻烦点定义结构体重载
};
vector<adj>List[N];//vector数组邻接表,存每个节点的所有邻接边
int dist[N],maxn;
bool visited[N];//因为每轮修改后dist变小的可能被纳入后,队列还残留未修改过和不是最小dist的同节点旧情况
priority_queue<adj,vector<adj>,cmp>q;//按边从小到大的优先队列(优先队列创建的另一种样,第二格填的是底层)
int djstr(int n)//源点编号
{
	fill(dist,dist+maxn+1,1e9);//距离一开始默认无边无穷远达不到人
	fill(visited,visited+maxn+1,0);//初始化
	dist[n]=0;//源点一开始肯定是要能抵达的
	adj t=new Edge;
	t->to=n,t->w=0;//初始化源点,其实也是类似从root到源点
	q.push(t);//通过优先队列的排序,减少每次更新完dist后还要再找最近的枚举过程
	while(q.empty()==false)
	{//每轮更新后,最近dist来自经过中转dist变小的点和未更改dist中最小的点
		t=q.top();//而放进优先队列里即可自动排序到堆顶取得
		q.pop();//源点A 中转点B 终点C
		int b=t->to;
		if(visited[b])//残留的非最佳选择的状态,节点已经被选上用完就跳过了
			continue;
		visited[b]=true;//已经使用纳入被采用集合
		for(int i=0;i<List[b].size();i++)
		{//遍历该点的邻接边  源点A 中转点B 终点C
			int c=List[b][i]->to;//第三个点C的编号
			if(visited[c]==false&&dist[b]+List[b][i]->w<dist[c])
			{//c点也必须没访问过才可以哦
				dist[c]=dist[b]+List[b][i]->w;//经过中转的距离都更短
				adj t2=new Edge;//a->c变近了所以有可能c成为下一次的选择点
				t2->to=c,t2->w=dist[c];
				q.push(t2);
			}
		}
	}
	int Min=1e9;
	for(int i=1;i<=maxn;i++)
		if(i!=n)
			Min=min(Min,dist[i]);
	return Min;
}
int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,m;//边的个数
	cin>>maxn>>m;
	for(int i=0;i<m;i++)
	{
		int a,b,w;
		cin>>a>>b>>w;
		adj t=new Edge;
		t->to=b,t->w=w;
		List[a].push_back(t);//a指向b
		t=new Edge;//要重新分配内存再赋值,不然改掉上面a加入的边属性了
		t->to=a,t->w=w;//无向图,就等于双向但不能直接改哦,因为这样改了就改掉上面加进去的值了
		List[b].push_back(t);//b指向a
	}
	cin>>n;
	cout<<djstr(n)<<'\n';
	for(int i=1;i<=maxn;i++)
		cout<<dist[i]<<' ';
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值