前言

如果您还不了解Dijkstra算法和Floyd算法,我建议您先跳过前言,直接学习算法的步骤和实现,再回过头阅读前言。

整理Dijkstra算法后发现,其本质就是在求第n步最短路径时,直接从第n-1步的结果中遍历得到(贪心),再在第n步最短路径求出的基础上对所有的最短路径更新(松弛);整理Floyd算法后发现,其本质就是在加入第n个顶点后,根据第n-1步的状态,求得第n步的最优状态(动态规划)。

究竟计算机是如何解决算法问题的呢?计算机的本质是一个状态机,内存里存储的所有数据构成了当前的状态,CPU只能利用当前的状态计算出下一个状态。当您想要解决一个问题时,其实就是在思考如何将这个问题表达成状态(用哪些变量存储哪些数据),以及如何在状态中转移(怎样根据一些变量计算出另一些变量)。所以空间复杂度就是支持计算所必须的存储状态最多有多少,时间复杂度就是从初始状态到最终状态中间需要多少步。

例如我们要求第99位的斐波那契数,我们知道第n位斐波那契数是由第n-1位和第n-2位相加得到的。求每一位斐波那契数时就是一个状态,新的数字需要之前的两个状态,所以在同一时刻需要保存两个状态。所以解决这个问题只需要从旧的状态里计算出新的状态,即a[n] = a[n - 1] + a[n - 2],不需要考虑是否需要更多的状态。对于这类解法,我们叫做递推。

斐波那契的例子具有明显的规律性,每一个时刻的状态数量是固定的,那么我们可以联想到,某种问题会在在同一时刻具有不同的状态数量。这就引出了阶段的概念,即在同一时刻具有不同的状态数量。以围棋为例,一步走一格,可以有上下左右四种选择(忽略重复棋子情况),所以棋子落下的位置存在很多情况。下棋的每一步就是一个阶段,从开始走了几步就是第几个阶段。

每个阶段的状态数量不定,并且一个阶段中的某个状态可以得到下个阶段中的一个或几个状态。那么我们计算出最终状态就要计算出每个阶段的某些状态。但事实上我们并不需要计算出所有的状态。例如在买苹果时,一个一个挑选,每次挑选就是一个阶段,每个阶段有不同的状态数量(可以挑选任意一个),在理论条件下,我们肯定会先挑选最好的,然后挑选次好的,再挑选次次好的...每次都挑选剩余苹果中最好的那一个,那么最后买的就是质量最好的。这个过程可以描述为“下一步的最优解是从当前最优中得到的”,这种算法就叫贪心。这种计算方法和斐波那契的例子一样,都是递推。

通过贪心我们可以解决很多类似买苹果的问题,其本质都是一个阶段的最优解可以由前一个阶段的最优中得到。那如果一个阶段的最优解无法从前一个阶段的最优中得到呢?斐波那契数的每一阶段需要前两个阶段的状态,这和只使用前一个阶段没有本质的区别,最复杂的情况是每一阶段状态求解需要前面所有阶段的状态。例如求解迷宫中起点到终点的最短路径,当你走到第n步,你必须保存第n步之前所有的位置,因为你可能会因碰壁而进行回溯,前n-1步是会影响接下来的选择的。这种前面的路线选择会影响到下一步的选择的性质就叫做后效性。迷宫问题的求解方式叫做搜索。

用搜索解决问题就好比遍历全部元素,找出我们想要的关系,比如简单选择排序。搜索的思路很好理解但是时间复杂度往往比较高,有一类问题的求解,并不需要前面所有状态(后效性成为了解决该类问题的契机)。联想求子串的问题,一开始最简单的思路就是逐个对比,会经历很多重复步骤。但是后来的KMP算法让我们认识到并不需要进行全部的对比,也就是可以不记录所有的状态。一个阶段的状态是由之前某个阶段状态得到(通常是通过之前阶段的状态计算得到一个状态转移方程),这样的思路就是动态规划。

我们总结一下这些思想:递推(每个阶段只有一个状态);贪心(每个阶段的最优状态都是由上一个状态的最优状态得到的);搜索(每个阶段的最优状态是由之前所有状态的组合得到的);动态规划(每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到)。

动态规划希望复用子问题的解,最好被反复依赖。其本质还是穷举,所以不知道哪个子问题的解会构成最优解,但知道这个子问题会被反复计算,所以把结果缓存起来。整个过程是树状的搜索过程。贪心希望每次都能排除一堆子问题。它不需要复用子问题的解,当前最优解从子问题最优解即可得到。整个过程是线性的推导过程。

解决问题就要看问题的特点选择解决方式,并不是所有的问题都能靠某种算法解决。

题目

Dijkstra算法

Dijkstra算法的目的是求单源点最短路径。设V0为源点,求出V0到其他点的最短路径长度。

算法步骤:

1、先求出V0到其他点的路径长度,V0->V1=13,V0->V2=8,V0->V3=max,V0->V4=30,V0->V5=max,V0->V6=32,将这些值存入dist数组。由于V0到V0的距离是已知的0,所以把V0标记为已确定的点(max意为不可到达)

2、选出dist数组中(除去已被确定的V0)最小值8,那么V0到V2的最短距离就是8,把V2标记为已确定的点。

3、以V2作为中转点,看V0是否能到达新的点,很明显V0->V3=13,并且V0到V3的距离小于max,那么将dist数组更新,V0->V3由max变为13。

4、选出dist数组中(除去已被确定的V0、V2)最小值13,那么V0->V1的最短距离就是13,把V1标记为已确定的点。

5、以V1作为中转点,看V0是否能到达新的点,很明显V0->V5=21、V0->V6=20。并且V0到V5、V6的距离都比原来小,那么将dist数组更新,V0->V5由max变为21,V0->V6由max变为20。

6、选出dist数组中(除去已被确定的V0、V2、V1)最小值13,那么V0到V3的最短距离就是8,把V3标记为已确定的点。

7、以V3作为中转点,看V0是否能到达新的点,很明显V0->V4=19,并且V0到V3的距离小于max,那么将dist数组更新,V0->V4由max变为19。

8、...

从上面的过程中可以看出23、45、67...这些步骤是相似的,所以可以写成循环。每次循环都确定新的点,由于V0一开始已被确定,所以要循环7-1=6次。

#include<stdio.h>
#define max 999int main()
{int i, v, min, minIndex;int flag[7] = { 0 }; // 标记数组int dist[7] = { 0 }; // 最短距离数组int map[7][7] = {    // 邻接矩阵{ 0, 13, 8, max, 30, max, 32 },{ max, 0, max, max, max, 9, 7 },{ max, max, 0 ,5 ,max ,max ,max },{ max, max, max, 0, 6, max, max },{ max, max, max, max, 0, 2, max },{ max, max, max, max, max, 0, 17 },{ max, max, max, max, max, max, 0 }};for (i = 0; i < 7; ++i) // 先V0周围点的距离dist[i] = map[0][i];flag[0] = 1;            // 把V0标记为已确定的点for (v = 0; v < 6; ++v) // 每次确定一个新的点,还需要确定6次{min = max;for (i = 0; i < 7; ++i) // 找出dist数组中最短距离{       if ((min > dist[i]) && (flag[i] != 1)) // 注意剔除已被确定的点{min = dist[i];minIndex = i;} }dist[minIndex] = min;  // 确定了V0到Vi的最短距离flag[minIndex] = 1;    // vi成为被确定的点for (i = 0; i < 7; ++i) // 更新dist数组{if (((min + map[minIndex][i]) < dist[i]) && (map[minIndex][i] < max)) // 中转点到其他点必须是可达的{dist[i] = min + map[minIndex][i];}}}for (i = 0; i < 7; ++i){printf("V0到V%d的最短距离为:%d\n", i, dist[i]);}getchar();return 0;
}

Folyd算法

Floyd算法的目的是求多源点路径,也可以说是求任意两点之间的最短路径。要想求单源点最短路径,只需在Dijkstra算法的基础上在加一层循环,时间复杂度就变成了O(n^3)。Dijkstra算法思想虽然简单,但是其实现是非常复杂,这样就占据了很多内存空间,接下来的Folyd算法时间复杂度也为0(n^3),但是代码量大大减小了。Folyd算法又叫插点法,其本质是动态规划,每插入一个点就更新一次dist数组。

算法步骤:

1、用邻接矩阵dist表示图(图的最终状态就是最短距离),没有直接相连的两点距离为max,自己到自己的距离设为0。max值不要设为INF,防止因相加导致溢出。

2、插入第0个点,观察是否因第0个点而导致其他点与点的距离发生变化,并更新dist。

3、插入第1个点,观察是否因第1个点而导致其他点与点的距离发生变化,并更新dist。

4、插入第2个点,观察是否因第2个点而导致其他点与点的距离发生变化,并更新dist。

5、以此类推...

其中更新dist所用的状态转移方程为dist[ v ][ j ] = min( dist[ v ][ j ], dist[ v ][ i ] + dist[ i ][ j ] )

#include<stdio.h>
#define max 999
#define min(a,b) (a<b)?a:bint main()
{int i, j, v;int dist[7][7] = {{0, 13, 8, max, 30, max, 32},{max, 0, max, max, max, 9, 7},{max, max, 0 ,5 ,max ,max ,max},{max, max, max, 0, 6, max, max},{max, max, max, max, 0, 2, max},{max, max, max, max, max, 0, 17},{max, max, max, max, max, max, 0}};for (v = 0; v < 7; ++v)for (i = 0; i < 7; ++i)for (j = 0; j < 7; ++j)dist[v][j] = min(dist[v][j], dist[v][i] + dist[i][j]);for (i = 0; i < 7; ++i){printf("V%d到其他点的最短路径长度为:",i);for (j = 0; j < 7; ++j){printf("%d ",dist[i][j]);}printf("\n");}getchar();return 0;
}

Dijkstra算法、Floyd算法的思想和实现(C语言)相关推荐

  1. 数据结构(六):图的概念、存储方式、基本操作、最小生成树、最短路径、有向无环图、关键路径 | Prim、Kruskal算法 | BFS、Dijkstra、Floyd算法 | 拓扑排序 | 求关键路径

    文章目录 第六章 图 一.图 (一)图的定义 (二)图逻辑结构的应用 (三)无向图.有向图 (四)简单图.多重图 (五)顶点的度.入度.出度 (六)顶点-顶点的关系描述 (七)连通图.强连通图 (八) ...

  2. 03 最短路 dijkstra算法spfa算法floyd算法(附带实例代码) 图论-1

    文章目录 最短路 邻接表的图如下 邻接矩阵如下图 链表实现邻接表实现代码 单源最短路径 Dijkstra 算法 朴素版本 Dijkstra 实现代码 堆优化的dijkstra算法代码实现 Bellma ...

  3. 图的应用—求解最短路径(BFS、Dijkstra和Floyd算法)

    BFS算法虽然可以求解最短路径问题,但是需要注意的是该算法只能求解非带权图的单源最短路径问题,或者说带权值相同且为1的图单源最短路径问题. 1.图的邻接矩阵存储结构定义 #define MaxVerN ...

  4. 最短路径算法——Dijkstra and Floyd算法

    一.     前言:     这个古老的算法应该耳熟能详了吧,但是我自从从学校出来到现在,最短路径算法都没有实际运用过,最近在一个GIS项目中总算用到了,于是乎把教材重温了下,同时查阅了网上很多的资料 ...

  5. 最短路算法 :Bellman-ford算法 Dijkstra算法 floyd算法 SPFA算法 详解

     本文链接   :http://www.cnblogs.com/Yan-C/p/3916281.html . 在本文中因为邻接表在比赛中不如前向星好写,而且前向星效率并不低所以,本文的代码 存图只 ...

  6. 最小环算法求解(Dijkstra算法+Floyd算法)

    方法一: #include<iostream> #include<algorithm> #include<cmath> #include<cstdio> ...

  7. 短小精悍的多源最短路径算法—Floyd算法

    前言 在图论中,在寻路最短路径中除了Dijkstra算法以外,还有Floyd算法也是非常经典,然而两种算法还是有区别的,Floyd主要计算多源最短路径. 在单源正权值最短路径,我们会用Dijkstra ...

  8. 只有5行的算法——Floyd算法

    Floyd算法 Floyd 的特别之处 算法设计 完整代码 相关题目 之前写了一篇Dijkstra算法详解,那是在趣学算法上的,不过却没在其中找到Floyd,现在这篇文章是啊哈算法里的Floyd算法讲 ...

  9. 最短路径算法——迪杰克斯拉算法/floyd算法

    最短路径算法--迪杰克斯拉算法 Dijkstra算法的思想 1.设置两个顶点集S和T,集合S中存放已经找到最短路径的顶点,集合T中存放着当前还未找到最短路径的顶点: 2.初始状态下,集合S中只包含源点 ...

  10. 最短路最基本算法———Floyd算法

    关于floyd算法 算法简介 实现思想 核心代码 后记 一.floyd简介 引自百度百科 在计算机科学中,Floyd-Warshall算法是一种在具有正或负边缘权重(但没有负周期)的加权图中找到最短路 ...

最新文章

  1. 转发和重定向的区别是什么
  2. Python将DataFrame的某一列作为index
  3. 收集最全的Joomla教材网站和joomla模板网站
  4. jquery日期时间控件
  5. jquery学习笔记四:ajax
  6. URAL 1934 Black Spot --- 最短的简单修改
  7. C++ 已知两点坐标和半径求圆心坐标程序
  8. -未来世界的幸存者- 读后感(现实篇和职业篇)
  9. c# 语音卡控制--语音卡实现电话录音
  10. 一鲸落万物生,公链还需看Hoo Smart Chain,六十余家项目报名万物生长计划
  11. 如何写一篇杀手级的软件工程师简历
  12. ng-content、ng-template、ng-container使用及区别
  13. 【程序厨】学习 Redis ,可以看看这个
  14. 计算机课玩手机检讨500,上学带手机检讨书500字范文(精选6篇)
  15. 定宣讲初稿,筑宣讲之基
  16. 【创意二维码】二维码挽救了传统的纸质报纸,这是怎么回事儿?
  17. 软件工程_东师站_第六周作业
  18. win10 mrt 找不到
  19. mysql语句:SET NAMES UTF8
  20. LeetCode题解(0594):计算最长和谐子序列(Python)

热门文章

  1. c语言程序设计教程龚尚福,C语言程序设计教程
  2. 《淘宝网开店 进货 运营 管理 客服 实战200招》——2.5 网上开店货源大揭密...
  3. WeTest明星工具-移动端性能测试PerfDog初探
  4. Displaying Bitmaps Efficiently总结
  5. 基于监控视频的前景目标提取-数学建模
  6. 中国高校人工智能专业综合排名:四大维度揭示72所高校AI专业综合实力
  7. 音乐播放器的歌曲进度调节和音量大小调节实现
  8. GRUB主题下载与设置
  9. 请用java写教务系统的排课算法
  10. 采样坦克4终极音色库-IK Multimedia SampleTank v4.08 MAX | 175G