贪心法——单源最短路径问题——dijkstra算法

贪心法——单源最短路径问题——dijkstra算法

单源最短路径

给定带权有向图G =(V,E),其中每条边的权是非负实数。另外,还给定V中的一个顶点,称为源。现在要计算从源到所有其它各顶点的最短路长度。这里路的长度是指路上各边权之和。这个问题通常称为单源最短路径问题。

dijkstra算法

思想: 初始顶点集合S为源点,每次扩充一个距离源点的特殊路径最短的点到该顶点集合S中,所谓特殊路径是指路径中的所有中间点都在集合S中的路径,S中的所有点都是已经找到最短路径的点,当所有点都加入S后,算法完成。

为什么最短的那个特殊路径就是对应的点的最短路径呢?可以用归纳法和反证法证明。

采用一个数组dis[]来记录所有点的最短特殊路径,一个数组vis[]来记录某个点是否被选择,每次选取未选择的点中特殊路径最短的那个点加入已选择集合,并更新dis[]:其它每个未选择的点到这个新加入的点的直接距离加上这个新加入点的最短特殊路径长度是否小于自身原来的最短特殊路径长度。

为了记录最短的路径是什么,一般而言是要根据每个终点记录其自身的路径,但是在这里可以只记录每个终点的前一个节点,然后倒推到源点,因为去掉最短路径的最后一个边所得到的的一定还是最短路径,因此需要一个前驱节点记录数组pre[]。

dijkstra算法只能处理权值非负的情况。

代码:

#include <bits/stdc++.h>

using namespace std;

#define MAXN 105
#define INF 0x3f3f3f

int m[MAXN][MAXN],vis[MAXN],dis[MAXN],pre[MAXN];

// dijkstra算法计算单源最短路径
void dijkstra(int s,int n){
    // 初始化
    memset(vis, 0, sizeof(vis));
    for(int i = 0;i < n;i++)
        dis[i] = m[s][i];
    memset(pre, s, sizeof(pre));//初始化为源点,正常情况下会全部被覆盖
    // 选取源点
    vis[s] = 1;
    dis[s] = 0;
    pre[s] = s;
    // 选取最小dis
    for(int i = 1;i < n;i++){
        int min = INF,p = -1;
        for(int j = 0;j < n;j++){
            if(vis[j]) continue;
            if(min > dis[j]){
                min = dis[j];
                p = j;
            }
        }
        // 假如找不到最小的dis
        if(p == -1){
            cout<<"error!"<<endl;
            return ;
        }
        // 将选取的点加入已选点集合中,并更新dis
        vis[p] = 1;
        for(int j = 0;j < n;j++){
            if(vis[j]) continue;
            // 假如更新了,那么pre就是p了,否则pre就是上一次的值
            // prime记录结果也是在更新处
            if(m[p][j]+dis[p] < dis[j]){
                dis[j] = m[p][j]+dis[p];
                pre[j] = p;
            }
        }
    }
}

int main(){
    int n = 5,s = 0;
    memset(m, INF, sizeof(m));
    for(int i = 0;i < n;i++)
        m[i][i] = 0;
    m[0][1] = 10;
    m[0][3] = 30;
    m[0][4] = 100;
    m[1][2] = 50;
    m[2][4] = 10;
    m[3][2] = 20;
    m[3][4] = 60;
    
    dijkstra(s, n);
    
    for(int i = 0;i < n;i++)
        cout<<i+1<<" "<<dis[i]<<endl;
    
    for(int i = 0;i < n;i++){
        cout<<i+1<<"->";
        int end = i;
        while(pre[end] != s){
            cout<<pre[end]+1<<"->";
            end = pre[end];
        }
        cout<<s+1<<endl;
    }   
}

复杂度:

时间复杂度:O(n2)

空间复杂度:O(n2)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
贪心法实现单源最短路径算法的基本思路是:从源点开始,每次选择一条当前最短的边,将该边的终点加入到已确定最短路径的顶点集合中,直到所有顶点都被加入到该集合中。 具体实现步骤如下: 1. 初始化:将源点加入到已确定最短路径的顶点集合中,将源点到各个顶点的距离初始化为无穷大,将源点到自身的距离初始化为0。 2. 选择当前最短的边:从源点出发,选择一条当前最短的边,将该边的终点加入到已确定最短路径的顶点集合中。 3. 更新距离:对于新加入的顶点,更新源点到该顶点的距离。具体方法是,将源点到已确定最短路径的顶点集合中的每个顶点的距离与该顶点到新加入的顶点的距离相加,如果得到的和小于源点到新加入的顶点的距离,则更新源点到新加入的顶点的距离。 4. 重复步骤2和3,直到所有顶点都被加入到已确定最短路径的顶点集合中。 下面是一个使用贪心法实现单源最短路径算法的Python代码示例: ```python def dijkstra(graph, start): # 初始化 dist = {v: float('inf') for v in graph} dist[start] = 0 visited = set() while len(visited) < len(graph): # 选择当前最短的边 current = min(set(dist.keys()) - visited, key=dist.get) visited.add(current) # 更新距离 for neighbor in graph[current]: if neighbor not in visited: new_dist = dist[current] + graph[current][neighbor] if new_dist < dist[neighbor]: dist[neighbor] = new_dist return dist ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值