图论:最短路——迪杰斯特拉算法

一.算法思想

初始化阶段‌

定义dis[i]表示起点k到节点i的最短路径长度
初始时dis[k]=0(起点到自身距离为0)
其他所有dis[i]=∞(表示尚未计算出最短路径)

‌集合划分‌

将图中节点分为两个集合:
已确定集S:最短路径已经暂时确定的节点
未确定集Q:尚未确定最短路径的节点
初始时S为空集,Q包含所有节点

迭代过程‌

while Q不为空时:
a. 从Q中选择dis值最小的节点u(即当前离起点最近的未确定节点)
b. 将u从Q移到S中(确认u的最短路径已找到)
c. 对u的所有邻居v进行松弛操作:
if dis[v] > dis[u] + w(u,v):
更新dis[v] = dis[u] + w(u,v)

正确性保证‌

贪心选择性质:每次选择dis最小的节点u,此时dis[u]就是最终最短距离
数学归纳法证明:
基础:起点k的距离0是正确的
假设:前n次选择的最短路径都正确
递推:第n+1次选择的节点u,其路径必然是最短的(反证法可证)
‌终止条件‌

关键特性‌

要求图中无负权边(否则贪心选择不成立)
时间复杂度:
普通实现:O(V²)
优先队列优化:O(E + VlogV)

二.代码

堆优化版本

O(E + VlogV)
适用于稀疏图,边数E小于V²


using namespace std;

typedef pair<int, int> pii; // (distance, node)

vector<int> dijkstra(vector<vector<pii>>& graph, int start) {
    int n = graph.size();
    vector<int> dist(n, INT_MAX);
    priority_queue<pii, vector<pii>, greater<pii>> pq;
    
    dist[start] = 0;
    pq.push({0, start});
    
    while (!pq.empty()) {
        int u = pq.top().second;
        int current_dist = pq.top().first;
        pq.pop();
        
        if (current_dist > dist[u]) continue;
        
        for (auto& edge : graph[u]) {
            int v = edge.first;
            int weight = edge.second;
            
            if (dist[v] > dist[u] + weight) {
                dist[v] = dist[u] + weight;
                pq.push({dist[v], v});
            }
        }
    }
    return dist;
}

为什么需要反复入队?
优先队列不支持动态更新
与其他最短路算法如SPFA的原理不一样,SPFA反复入队的原理是带反悔的贪心

当节点 v 的距离需要更新时(dis[v] = dis[u] + w(u,v)),无法直接修改队列中已有的 v 的旧距离值。

解决方案:直接将 v 和新距离插入队列(即使 v 已在队列中),队列中可能存在同一个节点 v 的多个副本(对应不同时刻的距离值)。

普通版本

O(V²)

void dijkstra(vector<vector<pair<int,int>>>& graph, int start) {
    int V = graph.size();
    vector<int> dist(V, INT_MAX);
    queue<int> q;
    
    dist[start] = 0;
    q.push(start);
    
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        
        for (auto& edge : graph[u]) {
            int v = edge.first;
            int weight = edge.second;
            
            if (dist[u] + weight < dist[v]) {
                dist[v] = dist[u] + weight;
                q.push(v); // 重新入队以传播更新
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值