vector邻接表建图+dijkstra模板

本文节点的编号从0开始计算
思路

dijkstra伪算法(未优化)

源点到自己的距离为0,其余点到源点的距离为无穷大
记录当前节点now为源点s
循环下面的步骤V次,V是结点的个数
    now标记已访问
    遍历now的出度(以now为起点出发的边)
        如果连向的节点(姑且记为to)未被访问,就“松弛”它
        松弛就是用“源点到to的距离”和“先到now再从now走到to的距离”的较小值更新前者
        伪代码 dis[to] = min( dis[to], dis[now] + cost[now][to] )
    按顺序遍历所有 “未被访问” 的节点
        找出离源点最近的那个,记录为pos
    now = pos

这个复杂度是O(N^2)

C++实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
using LL = long long;
const int inf = 0x3f3f3f3f;
const int MAX_E = 1e5+5; // 最大边数
const int MAX_V = 1e4+5; // 最大节点数
int V, E; // 点、边
struct edge{ int to, w; };
vector<edge> G[MAX_E];
bool vis[MAX_V];
int dis[MAX_V]; // 到源点的距离
void input();
void Build(int x, int y, int w);
void dijkstra(int s);
int main()
{
    input();
    dijkstra(0);
    return 0;
}

void Build(int x, int y, int w = 1)
{
    edge e; e.to = y; e.w = w;
    G[x].push_back(e);
}

void input()
{
    int a,b,w;
    edge e;
    cin >> V >> E;
    for (int i = 0; i < E; i++)
    {
        cin >> a >> b >> w; // 带权
        //cin >> a >> b; // 不带权
        Build(a, b, w);
        Build(b, a, w); // 无向
    }
}

void dijkstra(int s)
{
    memset(dis, inf, sizeof(vis)); dis[s] = 0;
    memset(vis, false, sizeof(vis));
    int now = s;
    for (int i = 0; i < V; i++) // 循环V次,i没有特殊意义
    {
        vis[now] = true;
        for (int j = 0; j < G[now].size(); j++) // 遍历now的出度
        {
            if (!vis[G[now][j].to]) // 如果第j条出度连向的节点未被访问
            {
                dis[G[now][j].to] = min( dis[G[now][j].to], dis[now] + G[now][j].w); // 松弛
            }
        }
        int Min = inf, pos = 0; // 找出未被访问的节点中距离最短的那个,这一步可以堆优化,即用优先队列避免遍历V次
        for (int i = 0; i < V; i++) // 如果结点从1开始,遍历[1,V]
        {
            if (!vis[i] && dis[i] < Min)
            {
                Min = dis[i];
                pos = i;
            }
        }
        now = pos;
    }
    for (int i = 0; i < V; i++) printf("%d->%d=%d\n", s, i, dis[i]);
}

在这里插入图片描述

借用书上的例图,并将它看作是有向图,当然只要把input函数里边的第二个Build注释掉就是有向图了。

输入

5 8
0 2 10
0 4 30
0 5 100
1 2 5
2 3 50
3 5 10
4 3 20
4 5 60

输出

0->0=0
0->1=15
0->2=10
0->3=50
0->4=30

堆优化
原理是优化“寻找未被访问的节点中离源点最近的那个”这一步。本来是遍历,所以复杂度是O(N*N),如果改用优先队列存储这些节点,就可以将复杂度改进为O(NlogN)

参考了https://www.cnblogs.com/Renyi-Fan/p/7508098.html

堆优化伪算法
首先定义新的数据类型node,存储点编号和点到源点的距离,也可以用pair代替
然后定义仿函数,为优先队列提供自定义的出队优先级

源点入队(入队的是node对象,含编号和距离)
当队列非空,循环下面步骤
	记录队首元素的点编号和距离 min_i, min_d
	出队
	如果min_i 被访问过,跳过这轮循环
	标记min_i已访问
	遍历min_i的出度
		记录第i条边连向的结点to,以及这条边的权w
		如果to已被访问,跳过此轮循环
		松弛
		node(to, dis[to]) 入队

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
using LL = long long;
using PII = pair<int, int>; // first记录点编号,second记录边权
const int inf = 0x3f3f3f3f;
const int MAX_E = 1e5+5; // 最大边数
const int MAX_V = 1e4+5; // 最大节点数
int V, E; // 点、边
struct edge{ int to, w; };
struct cmp
{
    bool operator () (const PII & P1, const PII &P2) const { return P1.second > P2.second; } // 距离短的优先
};
vector<edge> G[MAX_E];
bool vis[MAX_V];
int dis[MAX_V]; // 到源点的距离
void input();
void Build(int x, int y, int w);
void dijkstra(int s);
void dijkstra_2(int s); // 堆优化版本
int main()
{
    input();
    dijkstra(0);
    cout << endl;
    dijkstra_2(0);
    return 0;
}

void Build(int x, int y, int w = 1)
{
    edge e; e.to = y; e.w = w;
    G[x].push_back(e);
}

void input()
{
    int a,b,w;
    edge e;
    cin >> V >> E;
    for (int i = 0; i < E; i++)
    {
        cin >> a >> b >> w; // 带权
        //cin >> a >> b; // 不带权
        Build(a, b, w);
        Build(b, a, w); // 无向
    }
}

void dijkstra(int s) // 未优化
{
    memset(dis, inf, sizeof(vis)); dis[s] = 0;
    memset(vis, false, sizeof(vis));
    int now = s;
    for (int i = 0; i < V; i++) // 循环V次,i没有特殊意义
    {
        vis[now] = true;
        for (int j = 0; j < G[now].size(); j++) // 遍历now的出度
        {
            if (!vis[G[now][j].to]) // 如果第j条出度连向的节点未被访问
            {
                dis[G[now][j].to] = min( dis[G[now][j].to], dis[now] + G[now][j].w); // 松弛
            }
        }
        int Min = inf, pos = 0; // 找出未被访问的节点中距离最短的那个,这一步可以堆优化,即用优先队列避免遍历V次
        for (int i = 0; i < V; i++) // 如果结点从1开始,遍历[1,V]
        {
            if (!vis[i] && dis[i] < Min)
            {
                Min = dis[i];
                pos = i;
            }
        }
        now = pos;
    }
    for (int i = 0; i < V; i++) printf("%d->%d=%d\n", s, i, dis[i]);
}

struct node
{
    int i; // 节点编号
    int d; // 到源点的距离
    node(int ii = -1, int dd = inf) : i(ii), d(dd){}
};

struct Cmp // 仿函数
{
    bool operator()(const node & n1, const node & n2) { return n1.d > n2.d; } // 距离短的优先出队
};

priority_queue<node, vector<node>, Cmp> Q;

void dijkstra_2(int s) // 堆优化
{
    node n;
    int min_i, min_d; // 存距离源点最近的点编号和距离
    memset(dis, inf, sizeof(dis)); 
    memset(vis, false, sizeof(vis));
    dis[s] = 0;
    Q.push(node(s, dis[s]));
    while (!Q.empty())
    {
        n = Q.top();
        Q.pop();
        min_i = n.i;
        min_d = n.d;
        if (vis[min_i]) continue; // 如果 min_i 已被访问,不重复处理
        vis[min_i] = true;
        for (int i = 0; i < G[min_i].size(); i++) // 遍历 min_i 的边
        {
            int to = G[min_i][i].to; // min_i的第i条边连向的节点
            int w = G[min_i][i].w; // 边权
            if (!vis[to]) 
            { 
                dis[to] = min( dis[to], dis[min_i] + w ); // 松弛
                Q.push(node(to, dis[to]));
            }
        }
    }
    for (int i = 0; i < V; i++) printf("%d->%d=%d\n", s, i, dis[i]);
}

附一组测试数据
在这里插入图片描述

6 10
0 1 5
0 3 7
1 2 4
2 0 8
2 5 9
3 2 5
3 5 6
4 3 5
5 4 1
5 0 3

输出

0->0=0
0->1=5
0->2=8
0->3=7
0->4=4
0->5=3

0->0=0
0->1=5
0->2=8
0->3=7
0->4=4
0->5=3

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值