015-Dijkstra算法-贪心-《算法设计技巧与分析》M.H.A学习笔记

单源最短路问题:

在无向图 G=(V,E) 中,假设每条边E[i] 的长度为w[i],找到由顶点V0 到其余各点的最短路径。

 

Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,注意该算法要求图中不存在负权边。

 

基本思路:

a.初始时,S只包含源点,即S{v}v的距离为0U包含除v外的其他顶点,即:U={其余顶点},若vU中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。

 

b.U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是vk的最短路径长度)。

 

c.k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。

 

d.重复步骤bc直到所有顶点都包含在S中。

 

算法概要:


伪代码:

 


算法分析:

主要的耗费在第8步,查找最小标记的顶点耗费时间Θ(n),又第8步进行了n次,总的耗费是Θ(n2)。所以算法的时间复杂度是Θ(n2)

 

C++代码:

const int mxn = 200;
const int mxe = 10050;
int dis[mxn];         //点到起始点的距离。
int vis[mxn];         //标记点是否在s集合中。
int g[mxn][mxn];      //用邻接矩阵表示边。
int n, m;          
 
void dijkstra(int s)  //s为源点结果得出的dis[k]为s点到k点的距离
{
memset(vis, 0, sizeof(vis));       //也可循环只初始化0到n的点,节省时间
memset(dis, 0x3f, sizeof(dis));    //0x3f不好用于判断,可改为10e6
vis[s] = 1;
dis[s] = 0;                      //初始化1为起始点,在集合s中,与起始点距离为0.出1外的都置初值vis为0,dis为正无穷。
int now = s;
for (int i = 1; i < n; ++i)
{
//更新与now点邻接的点的标记
for (int j = 1; j <=n; ++j)
if (g[now][j])                                    //now与j点邻接
dis[j] = min(dis[j], dis[now] + g[now][j]);   //更新j到起始点的距离
//查找未访问过的标记最小的点作为now点
for (int j = 1; j <= n; ++j)
if (!vis[j] && (dis[j] < dis[now]||vis[now]) )   //若j点不在集合s中,且j到起始点的距离小于当前到起始点的距离(或now点已经在集合中)
now = j;                                      //更新当前的点
vis[now] = true;                                  //放入集合s
 }
}

堆优化:

堆优化可以把时间复杂度从Θ(n2)减少到O(mlogn)

 

我们用一个小顶堆来存储顶点的标记,主要的耗费在堆的操作上,每个堆运算耗费O(logn),算法共进行了n-1次插入、n-1次删除、m-n+1Siftup运算。总的时间复杂度为Omlogn)。

 

注意输入应该为邻接表,如果输入是邻接矩阵,得到的依旧是Θ(n2)的算法。

伪代码:

 

C++代码:

/*
node结构既用来保存边,也用来保存点编号-标记对。
*/
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int Ni = 10000;
const int INF = 1<<27;
struct node{
    int x,d;
    node(){}
    node(int a,int b){x=a;d=b;}
    bool operator < (const node & a) const
    {
        if(d==a.d) return x<a.x;
        else return d > a.d;
    }
};
vector<node> eg[Ni];
int dis[Ni],n;
void Dijkstra(int s)
{
    int i;
    for(i=0;i<=n;i++) dis[i]=INF;
    dis[s]=0;
    //用优先队列优化
    priority_queue<node> q;
    q.push(node(s,dis[s]));
    while(!q.empty())
    {
        node x=q.top();q.pop();
        for(i=0;i<eg[x.x].size();i++)
        {
            node y=eg[x.x][i];
            if(dis[y.x]>x.d+y.d)
            {
                dis[y.x]=x.d+y.d;
                q.push(node(y.x,dis[y.x]));
            }
        }
    }
}
int main()
{
    int a,b,d,m;
    while(scanf("%d%d",&n,&m),n+m)
    {
        for(int i=0;i<=n;i++) eg[i].clear();
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&d);
            eg[a].push_back(node(b,d));
            eg[b].push_back(node(a,d));
        }
        Dijkstra(1);
        printf("%d\n",dis[n]);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这波lucio来全学了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值