java最小生成树求最短路径_图(二)——最小生成树、最短路径问题

本文详细介绍了图的最小生成树问题,包括Prim算法和Kruskal算法,以及最短路径问题中的Dijkstra算法和Floyd算法。文章通过贪心算法和动态规划的角度探讨这些算法,解释了它们的工作原理和适用场景,并给出了具体的实现步骤和示例。
摘要由CSDN通过智能技术生成

图(二)——最小生成树、最短路径问题

第十一周课堂学习内容消化——图(二)(三)

本周课堂学习内容

图的遍历

最小生成树

最短路径问题

活动顶点与活动边问题

本周课堂中未理解透彻的地方

Prim算法

Kruskal算法

Dijkstra算法

Floyd算法

学习内容总结

贪心算法

研究完Prim算法、Kruskal算法和Dijkstra算法之后,我发现他们都属于贪心算法。因此,在要搞懂这两种算法之前,应该先弄明白贪心算法是什么。

一、简述贪心算法

所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。

贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。

所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。

二、贪心算法的基本思路

建立数学模型来描述问题。

把求解的问题分成若干个子问题。

对每一子问题求解,得到子问题的局部最优解。

把子问题的解局部最优解合成原来解问题的一个解。

三、贪心算法适用的问题

贪心策略适用的前提是:局部最优策略能导致产生全局最优解。

实际上,贪心算法适用的情况很少。一般,对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可做出判断。

动态规划

Floyd算法是一种典型的动态规划。在研究Floyd算法之前,我们先来研究一下这个动态规划。

一、简述动态规划

动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

动态规划的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。

与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。

二、动态规划的基本思路

分析最优解的性质,并刻画其结构特征。

递归的定义最优解。

以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值

根据计算最优值时得到的信息,构造问题的最优解

三、动态规划适用的问题

能采用动态规划求解的问题的一般要具有3个性质:

最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。

有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

Prim算法和Kruskal算法

弄明白贪心算法怎么回事之后,接下啦就开始挨个分析这几个算法啦~

一、算法简述

Prim算法和Kruskal算法都是实现最小生成树的算法,都属于贪心算法。

首先,先弄清最小生成树的概念与特点:

最小生成树及各边的权值总和最小的生成树。其中,若最小生成树n个顶点,那么他的边就有n-1个。若每个边的权值都是最小的,那么整体加起来就一定是最小的,这里并不存在后继型,因此在实现最小生成树时可以运用贪心算法。

接下来是Prim算法和Kruskal算法实现最小生成树的方法:

Prim算法:

核心思想:将点分为两拨,已经加入最小生成树的,和未加入的。找到未加入中距离集合最近的点,添加该点,修改其它点到集合的距离,直到所有结点都加入到最小生成树。

Kruskal算法:

核心思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路。其中判断是否回路为核心。

二、算法过程演示

以课上中给出的图为例:

36bab6e052904ea31f24110b09dd9ec9.png

Prim算法:

随机选择一个点作为初始点。这里我选择顶点A。

找到A周围权值最小的边(这里最小的是权值为1的A-C边),加入对应的顶点C。

将A-C看做整体后,再寻找这个整体周围权值最小的边。在这里是C-F边,加入顶点F。

重复以上步骤,由于若最小生成树n个顶点,那么他的边就有n-1个,因此循环n-1次即可

60815f3de518f1047e7015a62198725a.png

Kruskal算法:

首先将各边按照权值有小到大排序

判断回路:

A、B、C、D、E、F的下标分别为0,1,2,3,4,5

他们的初始值均先赋为-1

从最小的权值边开始,两个顶点随便设定一个作为根结点,将根结点的值减一,另一个顶点值改为根结点的下标

在加边的时候,若两个点的值相同,或者其中一个的值为根结点的下标,则会构成回路,应该排除这样的情况。

重复以上的步骤,至少需要循环判断n-1次,一直到找到n-1条符合条件的边才停止。

b1128c259030cf515dcb6ed841997b8f.png

Dijkstra算法和Floyd算法

一、算法简述

Dijkstra算法和Floyd算法都是广度优先搜索的算法,目的是解决最短路径问题。其中,Dijkstra算法也是一种贪心算法,而Floyd算法更多的是一种动态规划。

Dijkstra算法:

Dijkstra算法为最短路径中的单源最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。注意该算法要求图中不存在负权边。解决的是图中任意一个顶点到其他顶点的最短距离。

设G为赋权有向图或无向图,G边上的权均非负。

求G中从顶点u0到其余点的最短路。

S:具有永久标号的顶点集。

对每个顶点,定义两个标记(l(v) , z(v)),其中,

l(v):表示从顶点u0到v的一条路的权。

z(v):v的父亲点,用以确定最短路的路线。

算法的过程就是在每一步改进这两个标记,使最终l(v)为从顶点u0到v的最短路的权。输入为带权邻接矩阵W。

Floyd算法:

Floyd算法是各顶点对间最短路径算法,解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。

D(i , j):i 到 j 的距离

R(i , j):i 到 j 之间的插入点

输入带权邻接矩阵W,

赋初值:对所有 i , j ,d ( i , j ) ← w ( i , j ) , r ( i , j ) ← j , k←1。

更新 d ( i , j ) , r ( i , j ):对所有 i , j , 若 d ( i , k ) + d ( k , j ) <d ( i , j ) ,则

d ( i , j ) ← d ( i , j ) + d ( i , j ), r ( i , j ) ← k

若 k = v ,停止;否则 k ← k + 1,转2

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值