[算法读书笔记C++,搜索与图论]dijkstra算法

Posted 凌星An

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[算法读书笔记C++,搜索与图论]dijkstra算法相关的知识,希望对你有一定的参考价值。

介绍

Dijkstra算法是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法,是从一个顶点到其余各顶点的最短路径算法。

应用: 解决有权图中最短路径问题。
思想: 从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接点,直到扩展到终点为止。

下面,我们演示一下具体的过程
有向图 如下面所示:

起点(源点)为v1 ,终点为v4
从起点v1出开始遍历,标记v1已经被遍历过
遍历v1的邻接点,找到距离最近的邻接点 :v2
因为 v1->v2距离为7,v1->v4距离为70
从v2开始遍历,标记v2已经被遍历过了
遍历v2的邻接点,找到距离最近的邻接点 : v3
遍历v3的邻接点,找到距离最近的邻接点 : v4
故找到了v1到v4的最短路径为v1->v2->v3->v4 ,长度为18

伪代码:

const int N //图中节点个数
int dist[N]; //起点到节点i的路径长度
bool s[N];//当前节点是否已经去顶从起点到该店的最短路径
int  g[N][N];//邻接矩阵,存储图
int dijkstra(int start,int end)
    dist中起点位置初始化为0,其他位置初始化为∞(无穷大的数字)
    
    //遍历1~N节点
    for(int i=1;i<=N;i++)
           //把没有访问过的且距离起点最近的点赋值给t
           int t=-1;
           for(int j=1;j<=n;j++)
              if(!s[i]&&(t==-1||dist[t]>dist[j]))
                   t=j;
              
           
           //使用t更新邻接点的最短距离
           for(int j=1;j<=n;j++)
            if(dist[j]>dist[t]+g[t][j])
                dist[j]=dist[t]+g[t][j];
            
          
    
   //返回起点到终点的最短距离
   return dist[end];

题目练习

  1. Dijkstra求最短路 I

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。 请你求出 1 号点到 n 号点的最短距离,如果无法从 1
号点走到 n 号点,则输出 −1。
输入格式 第一行包含整数 n 和 m。 接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式 输出一个整数,表示 1 号点到 n 号点的最短距离。 如果路径不存在,则输出 −1。

数据范围 1≤n≤500, 1≤m≤105, 图中涉及边长均不超过10000。

输入样例: 3 3 1 2 2 2 3 1 1 3 4
输出样例: 3

思路:
建立图,使用dijkstra算法求取答案即可。
代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;


const int N=500+10;

//邻接矩阵
int g[N][N];
bool s[N];
int dist[N];

int dijkstra(const int n)
//初始化
    memset(dist,0x3f,sizeof dist);
    memset(s,0,sizeof s);
    dist[1]=0;
    for(int i=1;i<n;i++)
        //找不在s中的距离最近的点,赋值给t
        int t=-1;
        for(int j=1;j<=n;j++)
           if(!s[j]&&(t==-1||dist[j]<dist[t]))
             t=j;
           
        
        //将t加入s
        s[t]=true;
        //用t更新其他点的距离
        for(int j=1;j<=n;j++)
            if(dist[j]>dist[t]+g[t][j])
                dist[j]=dist[t]+g[t][j];
            
        
    
    //如果走不到终点,则终点距离并不会被初始化
    if(dist[n]==0x3f3f3f3f)
        return -1;
    
    return dist[n];


int main()
//初始化,没有边的两点距离为无穷大
    memset(g,0x3f,sizeof g);
    
    int n=0,m=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        int a=0,b=0,c=0;
        scanf("%d%d%d",&a,&b,&c);
        //两点之间可能包含重边,我们只需要保存最小的那条边
        g[a][b]=min(g[a][b],c);
    
    printf("%d\\n",dijkstra(n));
    return 0;

堆优化版本

在上面的dijkstra算法当中,时间复杂度为O(n^2);
倘若边数远小于n^2,我们需要对它进行优化的话, 遍历1~N节点 和 使用t更新邻接点的最短距离 这两方面,无法在进行简化。而对于 找不在s中的距离最近的点,赋值给t 我们可以使用堆来进行优化,取出最短路径的复杂度降为O(1);每次调整的复杂度降为O(elogn);e为该点的边数,所以复杂度降为O((m+n)logn)

伪代码

const int N //图中节点个数
const int M=N*N //图中有向边的数量
int dist[N]; //起点到节点i的路径长度
bool s[N];//当前节点是否已经去顶从起点到该店的最短路径
//邻接矩阵,存储图
int head[N],e[M],ne[M],w[M],idx=0;
int dijkstra(int start,int end)
    dist中起点位置初始化为0,其他位置初始化为∞(无穷大的数字)
    heap //小根堆
    heap.push(start);//把起点放入堆中
    //遍历1~N节点
    while(heap.size())
       //找到距离最近且没有被访问过的点
        int t=heap.top();
        if(s[t])
             continue;
        
        s[t]=true;
        //使用t更新邻接点的最短距离
        for(int i=head[t];i!=-1;i=ne[i])
             int j=e[i];
             if(dist[j]>dist[t]+w[i])
                dist[j]=dist[t]+w[i];
                heap.push(dist[j]);
             
        

   
   //返回起点到终点的最短距离
   return dist[end];

题目练习

  1. Dijkstra求最短路 II

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。 请你求出 1 号点到 n 号点的最短距离,如果无法从 1号点走到 n 号点,则输出 −1。

输入格式 第一行包含整数 n 和 m。 接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式 输出一个整数,表示 1 号点到 n 号点的最短距离。 如果路径不存在,则输出 −1。

数据范围 1≤n,m≤1.5×105, 图中涉及边长均不小于 0,且不超过 10000。

输入样例: 3 3 1 2 2 2 3 1 1 3 4
输出样例: 3

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;


const int N=1.5*1e5+10;
typedef pair<int,int>  PII;
//邻接矩阵
int head[N],w[N],e[N],ne[N],idx=0;

bool s[N];
int dist[N];

void add(int a,int b,int c)
    e[idx]=b,w[idx]=c,ne[idx]=head[a],head[a]=idx++;


int dijkstra(const int n)
    memset(dist,0x3f,sizeof dist);
    memset(s,0,sizeof s);
    dist[1]=0;
    
    priority_queue<PII,vector<PII>,greater<PII>>  heap;
    //距离   点
    heap.push(0,1);
    while(heap.size())
         //找不在s中的距离最近的点,赋值给t
        auto tmp=heap.top();
        heap.pop();
        int t=tmp.second,dis=tmp.first;
        if(s[t])
            continue;
        
        //将t加入s
        s[t]=true;
         //用t更新其他点的距离
        for(int i=head[t];i!=-1;i=ne[i])
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
                dist[j]=dist[t]+w[i];
                heap.push(dist[j],j);
            
        
       
    
    
    if(dist[n]==0x3f3f3f3f)
        return -1;
    
    return dist[n];


int main()
    memset(head,-1,sizeof head);
    int n=0,m=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        int a=0,b=0,c=0;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    
    printf("%d\\n",dijkstra(n));
    return 0;



总结

朴素dijkstra算法,复杂度为O(n^2) ,主要适用于稠密图,即边数m接近于n*n (n为点数) ,图采用邻接矩阵存储

堆优化dijkstra算法,复杂度为O((m+n)logn),主要使用于稀疏图,即边数m远小于n*n (n为点数) ,图采用邻接表存储

以上是关于[算法读书笔记C++,搜索与图论]dijkstra算法的主要内容,如果未能解决你的问题,请参考以下文章

第三章 搜索与图论

经典树与图论(最小生成树、哈夫曼树、最短路径问题---Dijkstra算法)

再谈排序与图论算法

noip图论需要弄懂啥?

《算法图解》读书笔记 - 狄克斯特拉算法(Dijkstra)

《算法图解》读书笔记 - 狄克斯特拉算法(Dijkstra)