python--数据结构--5种常用算法

1. 引言

据说有人归纳了计算机的五大常用算法,它们是贪婪算法,动态规划算法,分治算法,回溯算法以及分支限界算法。虽然不知道为何要将这五个算法归为最常用的算法,但是毫无疑问,这五个算法是有很多应用场景的,最优化问题大多可以利用这些算法解决。算法的本质就是解决问题。当数据量比较小时,其实根本就不需要什么算法,写一些for循环完全就可以很快速的搞定了,但是当数据量比较大,场景比较复杂的时候,编写for循环就是一个很不明智的方式了。一是耗时,二是写出的代码绝对是天书。当然还有第三点,这点也是最重要的,写代码是一种艺术,而不是搬砖。前面的文章里对这五种算法都已经做了详细的讲解和归纳,本文主要是一个总结,将这五种算法整理到一起来对比,分析一下。

2. 算法
0) 穷举法

穷举法简单粗暴,没有什么问题是搞不定的,只要你肯花时间。同时对于小数据量,穷举法就是最优秀的算法。就像太祖长拳,简单,人人都能会,能解决问题,但是与真正的高手过招,就颓了。

1) 贪心算法

贪心算法可以获取到问题的局部最优解,不一定能获取到全局最优解,同时获取最优解的好坏要看贪心策略的选择。特点就是简单,能获取到局部最优解。就像打狗棍法,同一套棍法,洪七公和鲁有脚的水平就差太多了,因此同样是贪心算法,不同的贪心策略会导致得到差异非常大的结果。
例如:普利姆算法、克鲁斯卡尔算法
具体的详细解析请参见下面的文章:
python–数据结构–贪心算法

2) 动态规划算法

当最优化问题具有重复子问题和最优子结构的时候,就是动态规划出场的时候了。动态规划算法的核心就是提供了一个memory来缓存重复子问题的结果,避免了递归的过程中的大量的重复计算。动态规划算法的难点在于怎么将问题转化为能够利用动态规划算法来解决。当重复子问题的数目比较小时,动态规划的效果也会很差。如果问题存在大量的重复子问题的话,那么动态规划对于效率的提高是非常恐怖的。就像斗转星移武功,对手强它也会比较强,对手若,他也会比较弱。
例如:斐波那契数列、0-1背包问题、国王和金矿问题、上楼梯问题
具体的详细解析请参见下面的文章:
python–数据结构–动态规划算法

3)分治算法

分治算法的逻辑更简单了,就是一个词,分而治之。分治算法就是把一个大的问题分为若干个子问题,然后在子问题继续向下分,一直到base cases,通过base cases的解决,一步步向上,最终解决最初的大问题。分治算法是递归的典型应用。
例如:折半查找、大整数乘法、归并排序、快速排序

4) 回溯算法

回溯算法是深度优先策略的典型应用,回溯算法就是沿着一条路向下走,如果此路不同了,则回溯到上一个
分岔路,在选一条路走,一直这样递归下去,直到遍历万所有的路径。八皇后问题是回溯算法的一个经典问题,还有一个经典的应用场景就是迷宫问题。
具体的详细解析请参见下面的文章:
python–数据结构–回溯算法

5) 分支限界算法

回溯算法是深度优先,那么分支限界法就是广度优先的一个经典的例子。回溯法一般来说是遍历整个解空间,获取问题的所有解,而分支限界法则是获取一个解(一般来说要获取最优解)。

3. 动态规划算法、贪心算法和分治算法特点

求最优解的问题,从根本上说是一种对解空间的遍历。最直接的暴力分析容易得到,最优解的解空间通常都是以指数阶增长,因此暴力穷举都是不可行的。

最优解问题大部分都可以拆分成一个个的子问题,把解空间的遍历视作对子问题树的遍历,则以某种形式对树整个的遍历一遍就可以求出最优解,如上面的分析,这是不可行的。最好的方法是对子问题树的一种修剪,比如动态规划算法、贪心算法。

子问题最优性:组成最优解的每一个子问题的解,对于这个子问题本身肯定也是最优的。

动态规划算法
  • 分阶段。
  • 每一阶段都可以分解出多个子问题。
  • 具有“子问题最优性”的性质。
  • 二者都将子问题的解合并,形成原问题的解。
  • 本质是对子问题树的一种修剪
  • 动态规划将分解后的子问题理解为相互间有联系,有重叠部分,需要记忆(避免对重复的子问题进行多次重复计算)。
  • 通常用迭代来做。对子问题树自底向上(从叶子向根)开始迭代
  • 动态规划方法代表了求最优解的问题的一般解法。我们自底向上(从叶子向根)构造子问题的解,对每一个子树的根,求出下面每一个叶子的值,并且以其中的最优值作为自身的值,其它的值舍弃。
  • 代价取决于可选择的数目(树的叉数)和子问题的的数目(树的节点数,或者是树的高度)。
  • 是一种递推算法,局部最优解推导全局最优解。
  • 一定是全局最优解,
  • 全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有最优解。
  • 具有无后效性
  • 决策随状态的变化而变化
贪心算法
  • 分阶段。
  • 每一阶段可分解出多个子问题。
  • 具有“子问题最优性”的性质。
  • 本质是对子问题树的一种修剪
  • 通常用迭代来做。对子问题树自顶向上(原问题作根)开始迭代
  • 贪心算法是动态规划方法的一个特例。贪心特在,可以证明,每一个子树的根的值不取决于下面叶子的值,而只取决于当前问题的状况。换句话说,不需要知道一个节点所有子树的情况,就可以求出这个节点的值。通常这个值都是对于当前的问题情况下,显而易见的“最优”情况。因此用“贪心”来描述这个算法的本质。由于贪心算法的这个特性,它对解空间树的遍历不需要自底向上,而只需要自根开始,选择最优的路,一直走到底就可以了。
  • 代价与动态规划相比,它的代价只取决于子问题的数目,而选择数目总为1。
  • 是一种递推算法,局部最优推导全局最优。
  • 不一定是全局最优解,因贪心算法的局部最优不一定能导致全局最优。
  • 作出的每步贪心决策都无法改变,因为贪心策略是由上一步的最优解推导下一步的最优解,而上一步之前的最优解则不作保留
  • 具有无后效性
  • 决策始终保持不变
分治算法
  • 分阶段。
  • 每一阶段可分解出 多个子问题。
  • 具有“子问题最优性”的性质。
  • 将子问题的解合并,形成原问题的解。
  • 分治法将分解后的子问题看成相互独立的。
  • 通过用递归来做。从子问题 树根 即原问题开始 递归 。
4. 补充
无后效性

转自https://blog.csdn.net/qq_30137611/article/details/77655707
无后效性是一个问题可以用动态规划求解的标志之一,理解无后效性对求解动态规划类题目非常重要。

某阶段的状态一旦确定,则此后过程的演变不再受此前各种状态及决策的影响

百度百科是这样定义的,是不是很苦涩,难懂。并且网上对这个名词的解释大多都是理论性的,不好理解,今天我们通过一个例子来看看什么是无后效性。

现在有一个四乘四的网格,左上角有一个棋子,棋子每次只能往下走或者往右走,现在要让棋子走到右下角

假设棋子走到了第二行第三列,记为s(2,3),如下图,画了两条路线和一条不符合题意的路线,那么当前的棋子[s(2,3)位置]怎么走到右下角和之前棋子是如何走到s(2,3)这个位置无关[不管是黑色尖头的路线还是蓝色箭头的路线] 。

换句话说,当位于s(2,3)的棋子要进行决策(向右或者向下走)的时候,之前棋子是如何走到s(2,3)这个位置的是不会影响我做这个决策的。之前的决策不会影响了未来的决策(之前和未来相对于现在棋子位于s(2,3)的时刻),这就是无后效性,也就是所谓的“未来与过去无关”。
在这里插入图片描述
看完了无后效性,那我们再来看看有后效性,还是刚才的例子,只不过现在题目的条件变了,现在棋子可以上下左右走但是不能走重复的格子

那么现在红色箭头就是一个合法的路线了,当我的棋子走到了s(2,3)这个位置的时候,要进行下一步的决策的时候,这时候的决策是受之前棋子是如何走到s(2,3)的决策的影响的,比如说红色箭头的路线,如果是红色箭头决策而形成的路线,那么我下一步决策就不能往下走了[因为题意要求不能走重复的格子],之前的决策影响了未来的决策,”之前影响了未来”,这就叫做有后效性



[该文章由以下文章拼凑总结而来]
动态规划与贪心算法的区别与联系
贪心算法和动态规划算法比较
分治与动态规划
五大常用算法总结
无后效性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值