单源最短路径的堆优化+链式前向星

普通迪杰斯特拉算法的复杂度为O(n^2),因为每次寻找当前最短路都要对所有的点进行遍历。当数据量大于1e5时,很有可能就会超时,因此可以针对寻找当前最短路的方法进行堆优化。
优化的思路就是建立一个优先队列,每次更新路径时将点压入,取最短路时直接取根就行。复杂度为O(eloge) 与边数有关。
链式前向星是邻接矩阵的一种保存方式,优点是可以携带更多的信息,比vector更快,使用的内存更少。

目录

  1. 链式前向星
  2. 最短路的堆优化

1.链式前向星

以下是个人的理解:

每个点都用一条链表来记录与之连接的点和边权,链表的插入方式为前插法。

先介绍链式前向星用到的两个数组:

1.edge:

定义如下:

struct edge
{
    int to; //与之连接的点的编号
    int dist; //边长
    int next; //另一条边的下标
};

一个点可以连接多条边,因此next的作用就是指向另一条共起点的边的下标,形成了一条链表。

2.head:

head[n]表示点n在这条链表中头节点的下标。如果没有点与其相连,则head[n]为-1(具体是多少跟题目有关,也可能为0)。

然后是关于如何添加边:

edge eg[MAXE];
int head[MAXN];
int cnt = 0;
void add_edge(int from, int to, int dist)
{
    eg[++cnt].to = to;
    eg[cnt].dist = dist;
    eg[cnt].next = head[from]; //下一节点更新为旧的头节点,如果链表为空,则next为-1
    head[from] = cnt;          //更新头节点
}

最后是关于如何遍历

for (int i = head[origin]; i != -1; i = eg[i].next)
{
    //....具体操作
}

以上就是关于链式前向星的全部介绍了,在数据量过大以致邻接矩阵会内存超限时建议使用链式前向星。

2. 最短路的堆优化

洛谷例题
这题如果用普通的Dijkstra算法会超时

#include <bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
const int inf = 2e9 + 5;
const int N = 1e5 + 5;
using namespace std;

struct edge //链式前向星方式存边
{
    int to;       //与之有边相连的点
    int next;     //下一条边的下标
    int distance; //边权
} e[N * 2];

struct node //堆优化的Dijkstra算法状态记录结构体
{
    int index;
    int dis;
    node() {}
    node(int i, int d) { index = i, dis = d; }
};
bool operator<(node a, node b) { return a.dis > b.dis; } //运算符重载,用于实现优先队列的排序

int head[N], cnt = 0;
int dis[N];
bool vis[N];

void addEdge(int u, int v, int dis)
{
    e[++cnt].to = v;
    e[cnt].distance = dis;
    e[cnt].next = head[u];
    head[u] = cnt;
}

void Dijkstra(int s, int n)
{
    for (int i = 1; i <= n; i++)
        dis[i] = inf;
    mem(vis, 0);
    priority_queue<node> q;
    q.push(node(s, 0));
    dis[s] = 0;
    while (q.size())
    {
        int x = q.top().index; //取堆顶的点作为下一个确定的点
        int d = q.top().dis;
        q.pop();
        if (vis[x]) continue; // x是确定的点,跳过
        vis[x] = 1;
        for (int i = head[x]; i; i = e[i].next)
        {
            int to = e[i].to;
            if (vis[to]) continue; //已经确定的点就跳过
            if (d + e[i].distance < dis[to])
            {
                dis[to] = d + e[i].distance;
                q.push(node(to, dis[to])); //将更新的点状态入堆
            }
        }
    }
}

int main()
{
    mem(head, 0);
    int n, m, s, u, v;
    int w;
    scanf("%d%d%d", &n, &m, &s);
    while (m--)
    {
        scanf("%d%d%d", &u, &v, &w);
        addEdge(u, v, w); //添加有向边
    }
    Dijkstra(s, n);
    for (int i = 1; i <= n; i++)
        printf("%d ", dis[i]);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值