首页 > 编程语言 >朴素迪氏最短单源路径的原理及C++实现

朴素迪氏最短单源路径的原理及C++实现

时间:2023-10-19 12:05:05浏览次数:41  
标签:vDis int 迪氏 路径 单源 C++ vector iSize iNode


Dijkstra算法,翻译为迪杰斯特拉或狄克斯特拉。在下驽钝,记不住如此长的翻译,故简称迪氏。

时间复杂度

O(n2),端点数的平方。

使用前提

边的权为正。可以非连通,非连通的距离为-1。

原理

源点到源点的最短路径只有一个节点{s}。除源点本身外,其它端点的最短路径至少有两个端点,整个路径{s...x2}可以拆分为两部分Path1={s...x1}和Path2={x2}。x2是最后节点,且Path1就是s到x1的最短路径。假定Path3是s到x1的最短路径,那Path3+Path2比Path1+Path2短,与Path1+Path2是最短路径矛盾。
按距离源点距离升序排序,第i个端点(i>0)的最短路径倒数第二个点一定取自[0,i)。x1不会等于x2,否则直接丢掉最后的x2。假定x1的最短距离第m大,m>i。那{s...x1}+{x2}显然比s{...x1}大,那么i > m,与m > i矛盾。

思路

两层循环,第一层循环依次处理,最短路径第i小的端点的最小路径。
第二层循环依次完成以下两个职责:
更新各点通过第i-1小端点到源点的距离。注意:已处理点,无需处理。如果距离变大无需处理。
求最短距离第i小的点。
m_vDis记录源点到各点的最短距离。
m_vPre记录各点最短路径的倒数第二个端点,方便获取最短路径。目的有二:一,增加理解性。二,某些场景会用到。
两个函数,分别通过邻接表和邻接矩阵获取最短路径。

核心代码

class CN2Dis
{
public:
    CN2Dis(int iSize) :m_iSize(iSize), DIS(m_vDis), PRE(m_vPre)
    {

    }
    void Cal(int start, const vector<vector<pair<int, int>>>& vNeiB)
    {
        m_vDis.assign(m_iSize, -1);
        m_vPre.assign(m_iSize, -1);
        vector<bool> vDo(m_iSize);//点是否已处理
        auto AddNode = [&](int iNode)
        {
            //const int iPreNode = m_vPre[iNode];
            long long llPreDis = m_vDis[iNode];

            vDo[iNode] = true;
            for (const auto& it : vNeiB[iNode])
            {
                if (vDo[it.first])
                {
                    continue;
                }

                if ((-1 == m_vDis[it.first]) || (it.second + llPreDis < m_vDis[it.first]))
                {
                    m_vDis[it.first] = it.second + llPreDis;
                    m_vPre[it.first] = iNode;
                }
            }

            long long llMinDis = LLONG_MAX;
            int iMinIndex = -1;
            for (int i = 0; i < m_vDis.size(); i++)
            {
                if (vDo[i])
                {
                    continue;
                }
                if (-1 == m_vDis[i])
                {
                    continue;
                }
                if (m_vDis[i] < llMinDis)
                {
                    iMinIndex = i;
                    llMinDis = m_vDis[i];
                }
            }
            return (LLONG_MAX == llMinDis) ? -1 : iMinIndex;
        };

        int next = start;
        m_vDis[start] = 0;
        while (-1 != (next = AddNode(next)));
    }
    void Cal(int start, const vector<vector<int>>& mat)
    {
        m_vDis.assign(m_iSize, LLONG_MAX);
        m_vPre.assign(m_iSize, -1);
        vector<bool> vDo(m_iSize);//点是否已处理
        auto AddNode = [&](int iNode)
        {
            long long llPreDis = m_vDis[iNode];
            vDo[iNode] = true;
            for (int i = 0; i < m_iSize; i++)
            {
                if (vDo[i])
                {
                    continue;
                }
                const long long llCurDis = mat[iNode][i];
                if (llCurDis <= 0)
                {
                    continue;
                }
                m_vDis[i] = min(m_vDis[i], m_vDis[iNode] + llCurDis);
            }
            long long llMinDis = LLONG_MAX;
            int iMinIndex = -1;
            for (int i = 0; i < m_iSize; i++)
            {
                if (vDo[i])
                {
                    continue;
                }
                if (m_vDis[i] < llMinDis)
                {
                    iMinIndex = i;
                    llMinDis = m_vDis[i];
                }
            }
            if (LLONG_MAX == llMinDis)
            {
                return -1;
            }

            m_vPre[iMinIndex] = iNode;
            return iMinIndex;
        };

        int next = start;
        m_vDis[start] = 0;
        while (-1 != (next = AddNode(next)));
    }
    const vector<long long>& DIS;
    const vector<int>& PRE;
private:
    const int m_iSize;
    vector<long long> m_vDis;//各点到起点的最短距离
    vector<int>  m_vPre;//最短路径的前一点
};

测试用例

#include <iostream>
 #include <vector>
 #include <queue>
 #include <assert.h>
 using namespace std;class CDebugN2Dis : public CN2Dis
 {
 public:
     using CN2Dis::CN2Dis;
     void Assert(const vector<int>& vDis)
     {
         for (int i = 0; i < vDis.size(); i++)
         {
             assert(vDis[i] == DIS[i]);
         }
     }
 };struct CDebugParam
 {
     int n;
     vector<vector<std::pair<int,int>>> edges;
     int s;
     vector<int> dis;//答案
 };int main()
 {
     vector<CDebugParam> params = { {1,{{}},0,{0}},
         {2,{{}},0,{0,-1}},{2,{{{1,2}},{{0,2}}},0,{0,2} }
         ,{3,{{{1,4},{2,5}},{{0,4}},{{0,5}}},0,{0,4,5} }
         ,{3,{{{1,4},{2,8}},{{0,4},{2,3}},{{0,8},{1,3}}},0,{0,4,7} }
         ,{3,{{{1,4},{2,8}},{{0,4},{2,5}},{{0,8},{1,5}}},0,{0,4,8} }
     };
     for (const auto& par : params)
     {
         CDebugN2Dis n2Dis(par.n);
         n2Dis.Cal(par.s, par.edges);
         n2Dis.Assert(par.dis);
     }
 }

测试环境

win10  VS2022  C++17

相关下载

源码及测试样例下载:

朴素迪氏最短单源路径的原理及C++源码及测试用例

【免费】朴素迪氏最短单源路径的原理及C++源码及测试用例

word版本讲解排版好

【免费】闻缺陷则喜之算法册C++实现资源

标签:vDis,int,迪氏,路径,单源,C++,vector,iSize,iNode
From: https://blog.51cto.com/u_15724537/7934108

相关文章

  • 有向图访问计数的原理及C++实现
    题目现有一个有向图,其中包含n个节点,节点编号从0到n-1。此外,该图还包含了n条有向边。给你一个下标从0开始的数组edges,其中edges[i]表示存在一条从节点i到节点edges[i]的边。想象在图上发生以下过程:你从节点x开始,通过边访问其他节点,直到你在此过程中再次......
  • Windows下VC++编译器32位memcpy、memmove函数汇编代码详解
    整理者:赤勇玄心行天道QQ号:280604597微信号:qq280604597QQ群:511046632博客:www.cnblogs.com/gaoyaguo  blog.csdn.net/cyz7758520?type=blog大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的!你可以随意转载,无需注明出处!写文档实属不易,我希望大家能支......
  • 深入实践C++11智能指针
    目录概念一、std::auto_ptr二、std::unique_ptr常用函数自定义智能指针对象持有的资源的释放函数三、std::shared_ptr常用函数四、std::enable_shared_from_this五、std::weak_ptr常用函数智能指针使用注意事项智能指针的简单实现概念C/C++语言最为人所诟病的特性之一就是......
  • C++常见入门题题解
    前言因为本人目前比较菜,所以给出的题解都是按照自己的学习进度来的,所以难度是一个循序渐进的过程,由于本人水平有限,望读者能够指出谬误,共同进步。回文数输出#include<bits/stdc++.h>//万能头usingnamespacestd;intmain(void){vector<int>font;//定义一个整型的向......
  • C++模板笔记
    参考文章:https://juejin.cn/post/7078530622527897631模板是C++的泛型编程机制,这种机制可以最大程度复用代码并且不会增加运行时开销模板分为函数模板和类模板函数模板函数模板是对函数的参数进行泛型化,传递给模板函数的类型实参可以是类,也可以是整型值,还可以是模板名比如://......
  • Qt/C/C++ 项目工程架构搭建设计经验
    摘要  独立负责一个Qt项目和经过前辈的教导后的一些架构搭建感悟,其中的各种理念对其他语言开发的项目也能有一定的互通,能带来更舒适的开发体验,谨代表个人开发的经验之谈,有需要的小伙伴酌情获取,辩证思考。也欢迎小伙伴们在评论区纠错补充。  关键词:C/C++、Qt、搭建框架、更......
  • 您需要了解的有关下一个MISRA®标准的信息:MISRA C++ 2023®简介
    我们在直播课中讲解过MISRAC通识及实践(请看VCR),今天我们来探讨一下MISRAC++:2023~ 【北汇信息|MISRAC通识及实践】 MISRAC++:2023®是广受期待的MISRAC++®标准的下一个版本,将于今年晚些时候发布。新版本将整合AUTOSARC++14指南,并支持C++的最新版本。 MISRA®是......
  • 学习C++
    概述:C++的基础语法主要包括变量、常量、数据类型、运算符、控制流语句等。下面分别进行介绍。变量和常量:变量是程序中用于存储数据的标识符,可以改变其值。在C++中,变量必须先声明后使用,声明的语法格式为“数据类型变量名”。例如,声明一个整型变量:inta。常量是在程序中不能改变......
  • C++ 获取文件信息(Linux)
    stat函数头文件:#include<sys/stat.h>intstat(constchar*restrictpathname,structstat*restrictbuf);第一个参数pathname:文件名,需要获取该文件的信息第二个参数buf:stat函数将pathname对应的文件信息,填入buf指向的stat结构中返回值:0成功;-1出错structstat{......
  • Qt/C++开源作品45-CPU内存显示控件/和任务管理器一致
    一、前言在很多软件上,会在某个部位显示一个部件,专门显示当前的CPU使用率以及内存占用,方便用户判断当前程序或者当前环境中是否还有剩余的CPU和内存留给程序使用,在不用打开任务管理器或者资源查看器的时候直接得知当前系统的运行情况。尤其是视频监控系统,如果64路全开,肯定很占用CP......