求最短路径

这篇博客介绍了单源最短路径问题,重点讲解了迪杰斯特拉算法的工作原理。迪杰斯特拉算法通过按路径长度递增的顺序寻找从源点到终点的最短路径,利用邻接矩阵表示有向图,并通过不断更新最短路径来逐步找到所有顶点的最短路径。

单源最短路径

从源点到终点的路径可能存在三种情况:1. 没有路径 2. 只有一条路径 3. 有多条路径

迪杰斯特拉(Dijkstra)

思路:按照路径长度递增的次序从源点到终点的路径。

  1. 假设 G r a p h [ ] [ ] Graph[][] Graph[][]为有向网的邻接矩阵, S S S为已找到最短路径结点的集合,其初始状态为只含一个顶点,即源点。另设一个一维数组 D i s t [ n ] Dist[n] Dist[n],其中每个分量表示当前所找到的从源点出发(经过集合S中的顶点)到各个终点的最短路径长度。显然, D i s t Dist Dist的初值为:
    D i s t [ k ] = G r a p h [ i ] [ k ] Dist[k] = Graph[i][k] Dist[k]=Graph[i][k]1
  1. 选择 u u u,使得
    D i s t [ u ] = m i n { D i s t [ w ] ∣ w ∉ S , w ∈ V ( G ) } Dist[u] = min\{Dist[w] | w ∉ S, w∈ V(G)\} Dist[u]=min{Dist[w]w/S,wV(G)}
    u u u为目前找到的从源点出发的最短路径的结点。将这些结点 u u u并入集合 S S S
  2. 修改 D i s t Dist Dist数组中所有尚未找到最终路径的结点的对应分量值。如果 G r a p h [ u ] [ w ] Graph[u][w] Graph[u][w]为有限值,即从顶点 u u u到顶点 w w w有弧存在,并且
    D i s t [ u ] + G r a p h [ u ] [ w ] < D i s t [ w ] Dist[u]+Graph[u][w] < Dist[w] Dist[u]+Graph[u][w]<Dist[w]
    则令:
    D i s t [ w ] = D i s t [ u ] + G r a p h [ u ] [ w ] Dist[w] = Dist[u] + Graph[u][w] Dist[w]=Dist[u]+Graph[u][w]
  3. 重复上述(2)和(3)的操作n-1次,即可求得从源点到所有终点的最短路径。

思路:copy自这个博客

#include<iostream>
#include<vector>
using namespace std;

void Dijkstra(int n, int s, const vector<vector<int> > &G, vector<int> &prev) {
    // initilization
    vector<int> dist(n, INT_MAX);
    vector<bool> S(n, false); // 判断是否在S集合中
    for (int i = 0; i < n; i++) {
        dist[i] = G[s][i];
        if (dist[i] == INT_MAX) {
            prev[i] = 0;
        }
        else {
            prev[i] = s;    // 有值的结点前驱结点一定是源点
        }
    }
    S[s] = true;
    dist[s] = 0;

    // find next path for the shortest path
    int u; // next
    int value = INT_MAX;
    for (int i = 0; i < n; i++) {
        if (!S[i] && dist[i] < value) {
            value = dist[i];
            u = i;
        }
    }
    
    // update
    for (int i = 0; i < n; i++) {
        if (!S[i] && dist[i] != INT_MAX) {
            int temp = dist[u] + G[u][i];
            if (temp < dist[i]) {
                dist[i] = temp;
                prev[i] = u;
            }
        }
    }
}

void searchPath(const vector<int>& prev, int s, int e) {
    vector<int> rpath;
    rpath.push_back(e);
    int temp = prev[e];
    while (temp != s) {
        rpath.push_back(temp);
        temp = prev[temp];
    }
    rpath.push_back(s);

    for (auto it = rpath.rbegin(); it != rpath.rend();it++) {
        if (it != rpath.rend())
            cout << *it << "->";
        else
            cout << *it << endl;
    }
}


int main() {
    int n, edge;
    vector<int> prev;
    vector<vector<int>> G(n, vector<int>(n,INT_MAX)) ;
    int st, en, weight;

    // input
    cin >> n >> edge;

    // Graph
    for (int i = 0; i < edge; i++) {
        cin >> st >> en >> weight;
        G[st][en] = weight;
        G[en][st] = weight;
    }

    Dijkstra(n, st, G, prev);
    
    int T;
    cin >> T;
    while (T--) {
        int v;
        cin >> v;
        searchPath(prev, st, en);
    }
}

佛洛依德(略)


  1. i i i为源点在途中的序号 ↩︎

Prim算法主要用于计算最小生成树,并非用于最短路径。该算法从某个顶点开始,将其纳入最小生成树结点集合V,剩余点构成待选择点集合U,然后找寻V中的点与U中的点组成的最短路径,把对应的点加入V集合并从U集合移除,重复此步骤直至U集合为空,并不聚焦于计算单个源点到其他所有顶点的最短路径 [^3]。 Kruskal算法同样是用于解最小生成树的算法,其思想是先把边按照权值排序,遍历的时候按照边的权值大小来取边,只要不形成回路,这条边就能使用,也不是用于最短路径的 [^4]。 而最短路径通常会使用如Dijkstra这类单源最短路径算法,它用于计算一个节点到其他所有节点的最短路径,以起始点为中心向外层层扩展,直到扩展到终点为止,不过要图中不存在负权边 [^2]。 ```python # 示例:Dijkstra算法简单实现 import heapq def dijkstra(graph, start): distances = {node: float('inf') for node in graph} distances[start] = 0 priority_queue = [(0, start)] while priority_queue: current_distance, current_node = heapq.heappop(priority_queue) if current_distance > distances[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances # 示例图的邻接表表示 graph = { 'A': {'B': 1, 'C': 4}, 'B': {'A': 1, 'C': 2, 'D': 5}, 'C': {'A': 4, 'B': 2, 'D': 1}, 'D': {'B': 5, 'C': 1} } start_node = 'A' result = dijkstra(graph, start_node) print(result) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值