dijkstra算法总结C++版

前言

  • 最近遇到求最短距离的题次数有点多,每次写都写不对,故总结一下,仅供参考。前言主要是对于自己遇到的疑问进行解释,希望有相同疑问的同学也能得到解答。
  • 对于dijkstra运用哪一种数据结构(邻接矩阵 OR 邻接表)。应考虑节点数量大小,网上查找资料说到不超过100用邻接表。
  • 对于大顶堆排序还是纯数组操作问题。大顶堆是优化后的方案。(具体可参考复杂度总结)
  • 核心思想:贪心思想
  • 如何记忆:链接: 看B站视频加深理解把~.

复杂度总结

邻接矩阵 O(n^2)
邻接表 O(n^2)
邻接表+binary heap O((n+m)logn)
邻接表+fibonacci heap O(m+nlogn)

邻阶矩阵 (适合稠密图,疏密图中空间浪费大,算法复杂度为O(n2))

leetcode743原题

要点注意

  • 在找【未找到最短距离的目标点的最小值】的两种写法(一个只定义x ,另一个定义最小值min = inf(无穷大))
class Solution {
public:
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
    /***************dijkstra************************************************************/
        /**邻接矩阵**/
        //定义默认值
        const int inf = 0x3f3f3f3f;
        //创建邻阶矩阵vector容器嵌套初始化
        vector<vector<int>> G(n,vector(n,inf));

        //对已经初始化的邻接矩阵 对times 中的元素进行复制,这样可以避免times 数据被破坏
        for(auto &t:times) {
            int x = t[0] - 1;//标记点从零开始
            int y = t[1] - 1;
            G[x][y] = t[2];
        }

        /****最短距离数组与布尔数组****/
        //创建 距离表dis
        vector<int> dis(n,inf);
        //自己与自己的距离为0
        dis[k-1] = 0;
        //创建 布尔表用于判断是否已经找到最短距离
        vector<bool> isF(n,false);

        //for(嵌套两个for)一个在还未找到最短距离的点中找最短,另一个for更新dis
        /***写法1******/
        for(int i = 0;i < n;i++) {
            int x = -1;
            for(int y = 0;y < n;y++) {
                if(!isF[y]&&(x == -1||dis[y]<dis[x])){
                    x = y; //起始 x == -1 说明先认为【未找到最短距离的点的第一个为最短】,之后发现又比他还短的作为最短。
                }
            }
          /************************************************************/
       /****上面写法1的另一种写法*****/
            /*与写法1的不同在于 该写法定义一个最小值进行if判断,对dis数组赋值,更新方式也不同
            for(int i = 0;i < n;i++) {
            int min = inf;
            for(int y = 0;y < n;y++) {
                if(!isF[y]&&(dis[y]<min){
                	min = dis[y];
                    x = y; //找到【未找到最短距离的目标点的最小值】
                }
            }
        
            */
            /************************************************************/
            
            //for循环结束后,当前x记录的目标点即为起始点到目标点的最短距离,将对应位置true
            isF[x] = true;
            //用for循环去更新最短距离
            for(int y = 0 ; y < n;y++) {
                dis[y] = min(dis[y],G[x][y]+dis[x]);//dis【y】:源点 到y点的(未证实)最短距离
            }
        }
          
/***************dijkstra************************************************************/
       
        //题目就是要找最短中的最远的距离
            int ans = *max_element(dis.begin(), dis.end());
            return ans == inf ? -1 : ans;
    }
};

邻接矩阵(优先级队列——又称堆排序)

  • 这里开始有点没理解~明天来理解一下if (!isFindShortest[aimPoint] && distanceS[aimPoint] > distanceS[NowStPoint] + cost) {
    解:就是在还未找到源点到【目标点=aimPoint】的最短距离点集里面逐个判断更新dis【】数组。
//迪杰拉特斯算法
void djlts() {
	//初始化起始村庄点到起始村庄点的消费为零 
	distanceS[startC] = 0;
	//创建优先级队列适配vector容器
	priority_queue<P, vector<P>, tmp2> pque;
	pque.push(P(startC, 0));
	while (!pque.empty()) {
		P p = pque.top();
		pque.pop();
		//初始化起始村庄点 
		int NowStPoint = p.first;
		if (isFindShortest[NowStPoint]) continue;//若已经找到最少消费 进入下一个循环

		
												 //当前节点虽然找到但未被使用,但要利用到这个节点的最小消费值, 所以在这里赋值为true
		isFindShortest[NowStPoint] = true;
		for (int i = 0; i < G[NowStPoint].size(); i++) {
			int aimPoint = G[NowStPoint][i].first;
			int cost = G[NowStPoint][i].second;
			/****这下面有点没理解 mark一下!!!**/
			//找到未被确定最小的消费中的最小值
			if (!isFindShortest[aimPoint] && distanceS[aimPoint] > distanceS[NowStPoint] + cost) {
				distanceS[aimPoint] = distanceS[NowStPoint] + cost;
				//将所有的都插入到优先级队列,由
				pque.push(P(aimPoint, distanceS[aimPoint]));
			}
		}
		
	}
}
原文链接:https://blog.csdn.net/yyjshang/article/details/121076937

邻接表版(适合非稠密图)

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
 
#define maxn 110  //最大顶点个数
int n;       //顶点个数
 
struct arcnode  //边结点
{
    int vertex;     //与表头结点相邻的顶点编号
    int weight;     //连接两顶点的边的权值
    arcnode * next; //指向下一相邻接点
    arcnode() {}
    arcnode(int v,int w):vertex(v),weight(w),next(NULL) {}
};
 
struct vernode      //顶点结点,为每一条邻接表的表头结点
{
    int vex;    //当前定点编号
    arcnode * firarc;   //与该顶点相连的第一个顶点组成的边
}Ver[maxn];
 
void Init()  //建立图的邻接表需要先初始化,建立顶点结点
{
    for(int i = 1; i <= n; i++)
    {
        Ver[i].vex = i;
        Ver[i].firarc = NULL;
    }
}
 
void Insert(int a, int b, int w)  //尾插法,插入以a为起点,b为终点,权为w的边,效率不如头插,但是可以去重边
{
    arcnode * q = new arcnode(b, w);
    if(Ver[a].firarc == NULL)
        Ver[a].firarc = q;
    else
    {
        arcnode * p = Ver[a].firarc;
        if(p->vertex == b)
        {
            if(p->weight > w)
                p->weight = w;
            return ;
        }
        while(p->next != NULL)
        {
            if(p->next->vertex == b)
            {
                if(p->next->weight > w);
                    p->next->weight = w;
                return ;
            }
            p = p->next;
        }
        p->next = q;
    }
}
void Insert2(int a, int b, int w)   //头插法,效率更高,但不能去重边
{
    arcnode * q = new arcnode(b, w);
    if(Ver[a].firarc == NULL)
        Ver[a].firarc = q;
    else
    {
        arcnode * p = Ver[a].firarc;
        q->next = p;
        Ver[a].firarc = q;
    }
}
struct node     //顶点节点,保存id和到源顶点的估算距离,优先队列需要的类型
{
    int id;     //源顶点id和估算距离
    int w;//权值
    friend bool operator<(node a, node b)   //因要实现最小堆,按升序排列,因而需要重载运算符,重定义优先级,以小为先
    {
        return a.w > b.w;
    }
};
 
#define INF 0xfffff    //权值上限
int parent[maxn];   //每个顶点的父亲节点,可以用于还原最短路径树
bool visited[maxn]; //用于判断顶点是否已经在最短路径树中,或者说是否已找到最短路径
node d[maxn];      //源点到每个顶点估算距离,最后结果为源点到所有顶点的最短路。
priority_queue<node> q; //优先队列stl实现

/***************Dijkstra**************************/
void Dijkstra(int s)    //Dijkstra算法,传入源顶点
{
    for(int i = 1; i <= n; i++) //初始化
    {
        d[i].id = i;
        d[i].w = INF;           //估算距离置INF
        parent[i] = -1;         //每个顶点都无父亲节点
        visited[i] = false;     //都未找到最短路
    }
    d[s].w = 0;                 //源点到源点最短路权值为0
    q.push(d[s]);               //压入队列中
    while(!q.empty())           //算法的核心,队列空说明完成了操作
    {
        node cd = q.top();      //取最小估算距离顶点
        q.pop();
        int u = cd.id;
        if(visited[u])   //注意这一句的深意,避免很多不必要的操作
            continue;
        visited[u] = true;
        arcnode * p = Ver[u].firarc;
        //松弛操作
        while(p != NULL)    //找所有与他相邻的顶点,进行松弛操作,更新估算距离,压入队列。
        {
            int v = p->vertex;
            if(!visited[v] && d[v].w > d[u].w+p->weight)
            {
                d[v].w = d[u].w+p->weight;
                parent[v] = u;
                q.push(d[v]);
            }
            p = p->next;
        }
    }
}
 /***************Dijkstra**************************/
int main()
{
    int m, a, b, c, st, ed;
    printf("请输入顶点数和边数:\n");
    scanf("%d%d", &n, &m);
    printf("请输入边以及权值(a, b, c)\n");
    Init();     //计算前必须初始化
    while(m--)
    {
        scanf("%d%d%d", &a, &b, &c);
        Insert2(a, b, c);   //无向图注意存储两条边
        Insert2(b, a, c);
    }
    printf("请输入起点和终点:\n");
    scanf("%d%d", &st, &ed);
    Dijkstra(st);
    if(d[ed].w != INF)
        printf("最短路径权值为:%d\n", d[ed].w);
    else
        printf("不存在从顶点%d到顶点%d的最短路径。\n", st, ed);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值