C++ Dijkstra 最短路径求解算法的两种实现方案

news/2024/7/15 17:34:08 标签: 算法, c++, 图论

迪杰斯特拉算法(Diikstra) 是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法

核心思想,搜索到某一个顶点后,更新与其相邻顶点的权重。顶点权重的数据含义表示从起始点到此点的最短路径长度(也就是经过的所有边的权重之和)。DJ 算法搜索时,每次选择的下一个顶点是所有权重值最小的顶点,其思想是保证每一次选择的顶点和当前顶点权重都是最短的。所以,DJ是基于贪心思想。

矩阵存储

常规时间复杂度:O(n),可以使用堆优化优先队列,时间复杂度降低到O(logN)。缺点是对于稀疏图而言空间浪费严重。

#include <bits/stdc++.h>
using namespace std;

//矩阵,存储图
int graph[100][100];
//顶点、边数
int v,e;
//优先队列,使用数组
int pri[100];
//存储起点到其它顶点之间的最短距离
int dis[100];
//设置无穷大常量
int const INF =INT_MAX;

/*
*初始化函数
*/
void init()
{
    //初始化图中顶点之间的关系
    for(int i=1; i<=v; i++)
    {
        for(int j=1; j<=v; j++)
        {
            if( i==j )
            {
                //自己和自己的关系(权重)为 0
                graph[i][j]=0;
            }
            else
            {
                //任意两点间的距离为无穷大
                graph[i][j]=INF;
            }
        }
    }

    //交互式确定图中顶点之间的关系
    int f,t,w;
    for( int i=1; i<=e; i++ )
    {
        cin>>f>>t>>w;
        graph[f][t]=w;

    }

    //初始设编号为 1 的顶点为起始点,根据顶点的关系初始化起点到其它顶点之间的距离
    for(int i=1; i<=v; i++)
    {
        dis[i]=graph[1][i];
    }

    //初始化优先队列(也称为候选队列)
    for(int i=1; i<=v; i++ )
    {
        if(i==1)
        {
            //起始顶点默认为已经候选
            pri[i]=1;
            continue;
        }
        //其它顶点都可候选
        pri[i]=0;
    }

}

/*
*
*Dijkstra算法
*/
void dijkstra()
{
    for(int i=1; i<=v; i++)
    {

        //从候选队列中选择一个顶点,要求到起始顶点的距离为最近的
        int u=-1;
        int mi=INF;
        for( int j=1; j<=v; j++ )
        {
            if(pri[j]==0 && dis[j]<mi)
            {
                mi=dis[j];
                u=j;
            }
        }

        
        if(u!=-1)
            //找到后设置为已经候选
            pri[u]=1;
        else 
            //找不到就结束
            break;

        //查找与此候选顶点相邻的顶点,且更新邻接点与起点之间的距离
       //相当于在此顶点基础上向后延长
        for( int j=1; j<=v; j++ )
        {

            if(  graph[u][j]!=INF )
            {
                //找到相邻顶点
                if(dis[j]>dis[u]+graph[u][j]  )
                {
                    //更新
                    dis[j]=dis[u]+graph[u][j];
                }
            }
        }

    }

}

/*
*
*显示最后的结果
*/
void show()
{
    for(int i=1; i<=v; i++)
    {
        cout<<dis[i]<<"\t";
    }
}


int main()
{
    cin>>v>>e;
    init();
    dijkstra();
    show();

    return 0;
}
//测试用例
6  9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
//输出
0  1  8  4  13  17

邻接表

整个时间复杂度可以优化到O(M+N)logN。在最坏的情况下M(边数)就是N(顶点数),这样的话(M+M)logN要比N还要大。但是大多数情况下并不会有那么多边,因此(M+M)logN要比N小很多。

#include <bits/stdc++.h>

using namespace std;
/*
* 顶点类型
*/
struct Ver
{
    //顶点编号
    int vid=0;
    //第一个邻接点
    int head=0;
    //起点到此顶点的距离(顶点权重),初始为 0 或者无穷大
    int dis=0;
    //重载函数
    bool operator<( const Ver & ver ) const
    {
        return this->dis<ver.dis;
    }

    void desc(){
      cout<<vid<<" "<<dis<<endl;
    }
};

/*
* 边
*/
struct Edge
{
    //邻接点
    int to;
    //下一个
    int next=0;
    //权重
    int weight;
};

class Graph
{
private:
    const int INF=INT_MAX;
    //存储所有顶点
    Ver vers[100];
    //存储所有边
    Edge edges[100];
    //顶点数,边数
    int v,e;
    //起点到其它顶点之间的最短距离
    int dis[100];
    //优先队列
    priority_queue<Ver> proQue;

public:
    Graph( int v,int e )
    {
        this->v=v;
        this->e=e;
        init();
    }
    void init()
    {
       for(int i=1;i<=v;i++){
              //重置顶点信息
            vers[i].vid=i;
            vers[i].dis=INF;
            vers[i].head=0;
       }
        int f,t,w;

        for(int i=1; i<=e; i++)
        {
            cin>>f>>t>>w;
            //设置边的信息
            edges[i].to=t;
            edges[i].weight=w;
            //头部插入
            edges[i].next=vers[f].head;
            vers[f].head=i;
        }

        for(int i=1; i<=v; i++)
        {
            dis[i]=vers[i].dis;
        }
    }

    void dijkstra(int start)
    {
        //初始化优先队列,起点到起点的距离为 0
        vers[start].dis=0;
        dis[start]=0;

        proQue.push(vers[start]);

        while( !proQue.empty() )
        {
            //出队列
            Ver ver=proQue.top();
            ver.desc();
            proQue.pop();

            //找到邻接顶点 i 是边集合索引号
            for( int i=ver.head; i!=0; i=edges[i].next)
            {
                int v=edges[i].to;
    //更新距离
                if( vers[ v ].dis > ver.dis + edges[i].weight )
                {
                    vers[ v ].dis = ver.dis+edges[i].weight;
                    dis[ v ]= vers[ v ].dis;
                    //入队列
                    proQue.push( vers[v] );
                }

            }

        }
    }

    void show()
    {
        for(int i=1; i<=v; i++)
        {
            cout<<dis[i]<<"\t";
        }
    }

};

int main()
{
    int v,e;
    cin>>v>>e;
    Graph graph(v,e);
    int s;
    cin>>s;
    graph.dijkstra(s);
    graph.show();
    return 0;
}

http://www.niftyadmin.cn/n/5147324.html

相关文章

python用cv2画图(line, rectangle, text等)

Python做图像图形研究的时候&#xff0c;通常需要画很多辅助几何形状&#xff08;比如bounding box等&#xff09;。基于opencv的几何图形绘制具有易用性&#xff0c;而且天然能和numpy数组交互。 本文总结了几种常用的cv2画几何图形的方法&#xff0c;当一个简易的手册使用&a…

使用 PDFView4NET 提供的方法来旋转页面

PDFView4NET 是一个用于在 Windows Forms 应用程序中显示和处理 PDF 文件的控件库。要旋转 PDF 文件&#xff0c;你可以使用 PDFView4NET 提供的方法来旋转页面。下面是一个示例代码&#xff0c;演示如何使用 PDFView4NET 旋转 PDF 页面&#xff1a; using O2S.Components.PDFV…

infercnv hpc东南服务器 .libpath 最终使用monocle2环境安装

安装不成功就用conda安装 conda install -c bioconda bioconductor-infercnv Installing infercnv There are several options for installing inferCNV. Choose whichever you prefer: Option A: Install infercnv from BioConductor (preferred) From within R, run the…

UnityAI——个体AI角色的操控行为脚本

注&#xff1a;本文用到了前文所用的基类UnityAI——操控行为编程的主要基类-CSDN博客 在一些游戏中&#xff0c;可能会遇到想让AI角色追逐或者避开玩家的情况。 如在飞机模拟游戏中&#xff0c;让导弹跟踪和进攻玩家或玩家的飞行器。这种情况下&#xff0c;可以运用本节介绍…

ROS分布式演练,多台设备进行通信的配置

1、概述 前面我们做的操作都是在单个设备上进行&#xff0c;也就是分别开启多个终端&#xff0c;在不同终端上启动节点等相关操作&#xff0c;这里我们使用两台设备来控制&#xff0c;一台虚拟机和一台无人车(使用VNC Viewer连上去&#xff0c;也可以看做一台Linux虚拟机) VNC…

青翼科技-国产化ARM系列TES720D-KIT

板卡概述 TES720D-KIT是专门针对我司TES720D&#xff08;基于复旦微FMQL20S400的全国产化ARM核心板&#xff09;的一套开发套件&#xff0c;它包含1个TES720D核心板&#xff0c;加上一个TES720D-EXT扩展底板。 FMQL20S400是复旦微电子研制的全可编程融合芯片&#xff0c;在单…

JavaScript中BOM与DOM

BOM window对象 所有的浏览器都支持window对象&#xff0c;他表示浏览器窗口&#xff0c; 所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员。 全局变量是 window 对象的属性。全局函数是 window 对象的方法。 接下来要讲的HTML DOM 的 document 也是…

学习笔记|多组率卡方检验和Fisher确切法|个案加权|规范表达|《小白爱上SPSS》课程:SPSS第十六讲 | 多组率卡方检验和Fisher确切法

目录 学习目的软件版本原始文档多组率卡方检验和Fisher确切法一、实战案例二、统计策略三、SPSS操作1、个案加权2、卡方检验 四、结果解读第一&#xff0c;分组统计描述结果第二&#xff0c;卡方检验。 五、规范报告1、规范表格2、规范文字 六、划重点 学习目的 SPSS第十六讲 …