迪杰斯特拉和弗洛伊德算法_迪杰斯特拉算法和弗洛伊德算法-程序员宅基地

技术标签: 算法  最短路径问题  图论  数据结构  

迪杰斯特拉和弗洛伊德算法

一、迪杰斯特拉算法:

1. dijkstra算法概述

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。 ——百度百科

注意该算法要求图中不存在负权边。

2. dijkstra算法原理

这个嘛,迪杰斯特拉百度百科,你可以直接看,如果还不太懂,且听细说:

比如在一个图中,有a, b, c, d, e这5个顶点,用邻接矩阵表示:

邻接矩阵

改:这个图中,a -> a、b -> b、… e -> e的距离应该为0而非无穷

  1. 我的理解,比如初始点为a, (用D(距离)数组记录初始点到所有点的距离)
    初始时,{INF, 3, 3, INF, INF} ——INF表示无穷大
    之后,将进行 (顶点数 - 1)次更新,那么如何更新呢?
    每次找出最小的值,比如在上图中的d(c也可),然后,重点来了
    将起点—到各个点的距离,分别与最小值对应点—到各个点的距离比较

  2. 解释一下:为什么要这么做?
    刚开始时,a到b的初始距离最小,那我就琢磨了,能不能通过这个边来更新距离,比如说a到d的距离为无穷大,那么我能不能先选一个最小的权值路径,到达那个顶点,看看从那个顶点能不能来到d,而且使得这两段路径(a => c =>d)的和比 a => d要小,显然是可以的,a -> d 初始为无穷大,然鹅,a可以先到c,a -> c = 3,c -> d = 5,故新的a -> d 的距离:
    D(d)= min(a - > d, a -> c + c -> d) = 8;

  3. 初始时的路径权值为无穷大代表的意思是起点和某一点不是直接相连的,所以是“无穷大”, 但是一旦发现可以借助中间点的“搭桥连线”,使得从起点(源点)可以访问到该点后,更新两点距离。不仅是无穷大的距离可以做这样的替换,可能起点和某点两点间存在直接相连的路径,但是由于这条路权值太大,反而不如走到另一点,再从另一点出发到上述的某点的两段权值和小,那么也可更新两点距离,这就是主要思想。

丸子

3. 关于路径输出

正如上述所言,一般情况下,初始路径:

如果源点(起点)到各个点的距离不是无穷大,以2. 原理中,邻接矩阵图为例,也就是说,从起点可以直接过去,那么初始时,Path[b]=Path[c]=a;

而为无穷大,说明终点为d、e,在当前状态下是不可能是从源点过来的,所以,Path[d] = Path[e] = -1;

另外,为了方便、统一标准,会把源点Path[a]设为-1,表示源点a前面没有点,这样在路径输出的时候就可以通过判断是否等于-1来停止输出

路径更新:遇到更小权值的路径,需要更新Path,比如初始时Path[e] = -1

但是,有了a=>b=>e这个短路径,那么将Path[e]改为b即可,因为Path[b] = a,所以路径Path[e] => Path[b]即Path[Path[e]] => Path[a],即为e->b->a

4. 完整代码展示
#include <bits/stdc++.h>

using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 100;

int D[N], S[N];
int Path[N];

typedef struct 
{
    
    int vex[N];
    int arcs[N][N];
    int vexnum, arcnum;

}AMGraph;

void initAndassign(AMGraph &G)
{
    
    cout << "请输入图的顶点和边数:\n";
    cin >> G.vexnum >> G.arcnum;
    for (int i = 0; i < G.vexnum; ++ i) G.vex[i] = i;
    for (int i = 0; i < G.vexnum; ++ i) {
    
        for (int j = 0; j < G.vexnum; ++ j)
            G.arcs[i][j] = INF;
    }
    cout << "请输入两边一权:\n";
    int x, y, w;
    for (int i = 0; i < G.arcnum; ++ i) {
    
        cin >> x >> y >> w;
        G.arcs[x][y] = w;//这是一个有向图
    }
}

void printPath(int v, AMGraph &G)
{
    
    if (v == -1) return;  
    printPath(Path[v], G);
    cout << 'v' << G.vex[v] << "->";
}
        
void dijiestla(AMGraph &G)
{
    
    int st = 0;
    initAndassign(G);
    cout << "请输入起点:" << '\n';
    cin >> st;//起点用st表示(start)
    for (int i = 0; i < G.vexnum; ++ i) {
    
        D[i] = G.arcs[st][i];
        if (D[i] < INF) Path[i] = st;
        else Path[i] = -1;
    }
    D[st] = 0; S[st] = 1; //st为起点,初始时到自身距离为0,并<-->并入集合中
    for (int i = 0; i < G.vexnum; ++ i) {
    
        int v = -1, m = INF;
        for (int k = 0; k < G.vexnum; ++ k) {
    
            if (!S[k] && D[k] < m) {
    
                v = k; m = D[k];
            }
        }
        if (v == -1) continue;
        S[v] = 1;//将v并入集合中
        for (int k = 0; k < G.vexnum; ++ k) {
    
            if (!S[k] && D[v] + G.arcs[v][k] < D[k]) {
    
                D[k] = D[v] + G.arcs[v][k];
                Path[k] = v;
                // cout << "Path["<< k << "] = " << v << '\n';
            }
                
        }
    }
    for (int i = 0; i < G.vexnum; ++ i) {
    
        cout << "最短路径为:"; 
        printPath(i, G);
        cout << "最小路径长度为:" << D[i] << '\n';
         
    }
    //for (int i = 1; i <= G.vexnum; ++ i) cout << D[i] << ' ';
    cout << '\n';
}

int main()
{
    
    AMGraph G;
    dijiestla(G);
    system("pause");

    return 0;
}
/*
两组测试材料,对应图1、图2
7 12
0 1 9
0 2 7
0 3 2
2 1 5
2 3 2
3 2 4
1 4 5
3 5 3
5 1 3
5 4 11
4 6 6
5 6 9

6 8
0 2 10
0 4 30
0 5 100
1 2 5
2 3 50
3 5 10
4 3 20
4 5 60
*/
5. 测试图例及结果

图1:

d

t1

图2:

y

t2

z

二、弗洛伊德算法:

1. floyed算法概述

核心:通过一个图的权值矩阵求出它的每两点间的最短路径矩阵

通俗来讲,弗洛伊德算法可以求图中任意两点的最短路径的值以及打印其路径

2. floyed算法原理

与迪杰斯特拉算法一样,都是采用动态规划的思想。算法过程:

  1. 从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。

  2. 对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。把图用邻接矩阵G表示出来,如果从Vi到Vj有路可达,则G[i] [j] = d,d表示该路的长度;否则G[i] [j] = 无穷大。定义一个矩阵D用来记录所插入点的信息,D[i] [j]表示从Vi到Vj需要经过的点,初始化D[i] [j] = j。把各个顶点插入图中,比较插点后的距离与原来的距离,G[i] [j] = min( G[i] [j], G[i] [k]+G[k] [j] ),如果G[i][j]的值变小,则D[i] [j]=k。在G中包含有两点之间最短道路的信息,而在D中则包含了最短通路径的信息。比如,要寻找从V5到V1的路径。根据D,假如D(5,1)=3则说明从V5到V1经过V3,路径为{V5,V3,V1},如果D(5,3)=3,说明V5与V3直接相连,如果D(3,1)=1,说明V3与V1直接相连。 [4]

balabala…其实对floyed算法,想要学好,需要先记住步骤,然后(默写)写代码,最后再在纸面上画一画。你可能会说:“不就是背吗?”,我想说的是:对的,学习第一步是记忆。最后在纸上演示是为了加深理解。

学习是一个由表及里的过程,如果学算法上来就让你弄懂原理,再去写代码,这就违背了这个道理。但是,不能一概而论,容易理解的肯定可以从原理出发,但我想说的是实在不明白的,就需要降低一些要求,先掌握考试、代码,和基本思路、步骤。至于根源,闲时有余力再探!

——以上仅个人观点

3. 关于路径输出问题

关于路径怎么变,看代码就行。

比如,输出v0->v5的路径,那么输出格式为,

Path[v0] [v5] -> Path[v0] [Path[v0] [v5]] ,也就是说,当输出起点和终点进入时,Path第一维不变,始终为起点,第二维在不断更新,当第二维与第一维的数相等,停止输出路径

void printPath(int v1, int v2)
{
    
    int z = Path[v1][v2];
    if (z != v1) printPath(v1, z, G);
    cout << z << "=>" << v2 << ' ';
}
4. 完整代码展示
#include <bits/stdc++.h>

using namespace std;

const int N = 100;
const int INF = 0x3f3f3f3f;

int D[N][N];
int Path[N][N];

typedef struct 
{
    
    int vex[N];
    int arcs[N][N];
    int vexnum, arcnum;

}AMGraph;

void initAndassign(AMGraph &G)
{
    
    cout << "请输入图的顶点和边数:\n";
    cin >> G.vexnum >> G.arcnum;
    for (int i = 0; i < G.vexnum; ++ i) G.vex[i] = i;
    for (int i = 0; i < G.vexnum; ++ i) {
    
        for (int j = 0; j < G.vexnum; ++ j)
            if (i != j) G.arcs[i][j] = INF;
    }
    cout << "请输入两边一权:\n";
    int x, y, w;
    for (int i = 0; i < G.arcnum; ++ i) {
    
        cin >> x >> y >> w;
        G.arcs[x][y] = w;//这是一个有向图
    }
    for (int i = 0; i < G.vexnum; ++ i) {
    
        for (int j = 0; j < G.vexnum; ++ j) {
    
            D[i][j] = G.arcs[i][j];
            if (D[i][j] < INF && i != j) Path[i][j] = i;
            else Path[i][j] = -1;
        }
    }
}

void printPath(int v1, int v2, AMGraph &G)
{
    
    int z = Path[v1][v2];
    if (z != v1) printPath(v1, z, G);
    cout << 'v' << G.vex[z] << "=>" << 'v' << G.vex[v2] << ' ';
}

void floyed(AMGraph &G)
{
    
    initAndassign(G);
    for (int k = 0; k < G.vexnum; ++ k) {
    
        for (int i = 0; i < G.vexnum; ++ i) {
    
            for (int j = 0; j < G.vexnum; ++ j) {
    
                if (D[i][k] + D[k][j] < D[i][j]) {
    
                    D[i][j] = D[i][k] + D[k][j];
                    Path[i][j] = Path[k][j];
                }
            }
        }
    }
    //Path矩阵
    for (int i = 0; i < G.vexnum; ++ i) {
    
        for (int j = 0; j < G.vexnum; ++ j) {
    
            cout << Path[i][j] << ' ';
        }
        cout << '\n';
    }
    cout << '\n';
    
    return;
}

int main()
{
    
    AMGraph G;
    floyed(G);
    cout << "输入起点和终点:" << '\n';
    int v1, v2;
    cin >> v1 >> v2;
    printPath(v1, v2, G);
    cout << '\n';
    
    system("pause");
    return 0;
}
/*
4 8
0 1 1
0 3 4
1 2 9
1 3 2
2 0 3
2 1 5
2 3 8
3 2 6
*/
5. 测试图例与结果

f
在这里插入图片描述
在这里插入图片描述

l

(累死了…www~)

THE END

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_51340322/article/details/119333825

智能推荐

转载:十款主流科研绘图软件-程序员宅基地

文章浏览阅读6.4k次,点赞2次,收藏15次。2_科研绘图软件

关于rk3588s使用facenet-pytorch-main进行onnx的转换以及RKNN生成操作_reducel2-程序员宅基地

文章浏览阅读2k次,点赞2次,收藏13次。关于rk3588s使用facenet-pytorch-main进行onnx的转换以及RKNN生成操作_reducel2

aar打包依赖 android_android中怎么把module打包成aar文件,以及怎么使用?-程序员宅基地

文章浏览阅读467次。何为aar包?jar与aar的简单区别:*.jar:只包含了class文件与清单文件 ,不包含资源文件,如图片等所有res中的文件。*.aar:包含所有资源 ,class 以及 res 资源文件全部包含一、在android studio中新建moudle1、新建module或者导入:file->new->import->new module/import module新..._android 模块化开发打包成aar再调用

django 查询条件 正则查找regex 200316_django正则查询-程序员宅基地

文章浏览阅读393次。正则查找查找正则以某开头的_django正则查询

SSD cache命中率跟IOPS_硬盘命中率-程序员宅基地

文章浏览阅读1.3k次。对于ssd-cache来说,一个非常重要的指标是命中率,而客户真正关心的实际上是IOPS性能,那么命中率跟IOPS的关系怎样呢?且看下面的分析。如果一个虚拟机的命中率是80%,另外一个虚拟机命中率是90%,那么他们的性能(IOPS)相差多少呢?凭直觉,他们的IOPS应该是相差10%,那么实际上是不是这样呢?假如SSD磁盘单线程读的IOPS是5000,7200转的机械磁盘的IOPS是80,那么SSD每次读需要 1000ms/5000=0.2ms,机械磁盘每次读需要1000ms/80=12.5ms1_硬盘命中率

Danmo的学习之路(ajax)-程序员宅基地

文章浏览阅读118次。加油

随便推点

Oracle 官方Java Jdk1.8_API帮助文档+Android 开发帮助文档(中英文版)_oracle官网下载的jdkapi没中文的吗-程序员宅基地

文章浏览阅读1.6k次。Oracle 官方 Java JDK1.8_API 帮助文档(英文)JDK 1.8 API 谷歌翻译版 密码:yupnAndroid API 开发文档 (中文版)密码:nhc4Windows系统下阅读CHM:参考 百度经验Mac 阅读CHM格式的文档推荐:CHM Read App Store有下载出现乱码:解决方案:显示-文档编码-Unicode(UTF-8) ..._oracle官网下载的jdkapi没中文的吗

[一天一项目]统计元音字母_用switch语句编写程序,统计输入的一串字母中元音字母(a,e,i,o,u)的总个数和每个元-程序员宅基地

文章浏览阅读1.2k次。统计元音字母——输入一个字符串,统计处其中元音字母的数量。更复杂点的话统计出每个元音字母的数量。统计元音字母,其实和拉丁猪文字游戏有异曲同工之妙,算法其实差不多,但是统计元音字母有两种理解方式:计算总的元音字母出现次数计算每个元音字母出现的次数下面列出两种解决方法。//如果不需要具体区分每个元音字母出现的次数 private static void count(String conte_用switch语句编写程序,统计输入的一串字母中元音字母(a,e,i,o,u)的总个数和每个元

实验7-1-7 查找整数 (10分)_【id:412】【11分】g. 实验7-1-7 查找整数 (10 分) 题目描述 本题要求从输入的n-程序员宅基地

文章浏览阅读2.7k次,点赞3次,收藏2次。本题要求从输入的N个整数中查找给定的X。如果找到,输出X的位置(从0开始数);如果没有找到,输出“Not Found”。输入格式:输入在第一行中给出两个正整数N(≤20)和X,第二行给出N个整数。数字均不超过长整型,其间以空格分隔。输出格式:在一行中输出X的位置,或者“Not Found”。输入样例1:5 73 5 7 1 9输出样例1:2输入样例2:5 73 5 8 1 9输出样例2:Not Found#include<stdio.h>int main(){_【id:412】【11分】g. 实验7-1-7 查找整数 (10 分) 题目描述 本题要求从输入的n个

Jetpack Compose中的副作用_disposableeffect-程序员宅基地

文章浏览阅读2.3k次,点赞4次,收藏7次。从本质上讲,副作用是任何超出函数控制和作用域的东西。副作用会使函数变得不确定,因此它们使开发人员难以推理代码。这对于React、Compose这类的声明式UI框架至关重要,因为它们都是通过函数(组件)的反复执行来渲染UI的,函数执行的时机和次数都不可控,但是函数的执行结果必须可控,因此,我们要求这些函数组件必须用纯函数实现。_disposableeffect

大数据毕业设计Hadoop+Spark+Hive景区游客满意度预测与优化 旅游推荐系统 Apriori算法 景区客流量预测 旅游大数据 旅游景点推荐 景点规划 计算机毕业设计 机器学习 深度学习-程序员宅基地

文章浏览阅读715次,点赞8次,收藏10次。大数据毕业设计Hadoop+Spark+Hive景区游客满意度预测与优化 旅游推荐系统 Apriori算法 景区客流量预测 旅游大数据 旅游景点推荐 景点规划 计算机毕业设计 机器学习 深度学习

开发ROS机器人的自动驾驶功能-程序员宅基地

文章浏览阅读302次,点赞7次,收藏10次。1.背景介绍自动驾驶技术是近年来迅速发展的一领域,它涉及到计算机视觉、机器学习、人工智能等多个领域的技术。在这篇文章中,我们将讨论如何使用Robot Operating System(ROS)开发机器人的自动驾驶功能。自动驾驶技术的目标是让机器人无人干预地完成驾驶任务,提高交通安全和效率。ROS是一个开源的软件框架,用于开发和控制机器人系统。它提供了一系列的库和工具,使得开发者可以轻松地构...

推荐文章

热门文章

相关标签