【python】手把手带你掌握算法基础01_二分法和选择排序法的实现

一、大O表示法

学算法就绕不开大O表示法,大O法可以告诉我们算法的时间和空间复杂度。

我们先聊点简单的,请你从1数到10,这里的复杂度是多少呢?O(10)。那么从1数到n,归纳法告诉我们,复杂度就是O(n)。n就是操作数,也就是你在数数过程中做了n个动作的意思。

为什么我们在大O中只讲究操作数,而不是具体的时间呢?因为一个动作所消耗的常量时间系数,对于算法而言是微不足道的。举个例子,你希望从一个数组中查找某个值,用顺序遍历(也就是一个个找过去),我们需要耗费n步,但用二分法,我们只需要耗费log n步(这是由于每进行一个动作,我们都可以摒弃一半的数据,2^x=n —> log n=x)。

如果一个数组中有2^32个数,那么用顺序遍历,最糟糕时候,我们需要2^32个动作才可以找到这个数;而使用二分法,我们只需要log(2^32)=32个动作就可以找到这个数。假设顺序遍历中,一个动作需要耗费10毫秒,二分法中,一个动作需要耗费1秒。如果有8个动作,用顺序遍历,我们需要耗费0.08秒,用二分法,我们则需要3秒(2^3=8个动作)。那么如果有2^32个动作呢,用顺序遍历,我们需要2^32*10毫秒,约等于50天,而用二分法,我们则只需要32秒即可。相对于算法处理的量级来说,大部分情况下,常量时间系数都是次级的。

二、二分法

二分法对于查找某数来说,是最为快捷的方法,但使用二分法有一个前提条件,必须是有序数列。有序数列是什么意思呢?要不从小至大,要不从大至小排列。这样,我们才可以找出数组中间的那个数mid,和目标数item进行大小比对:如果mid比item大,那么我们就摒弃比mid大的那一半数据,以此类推,直到我们找到item,或者数组中没有item。

python实现如下:


二分法实现

几点说明:

①python3中,int / int会变成一个浮点数哦,所以我们需要用math中的floor函数,对这个浮点数进行向下取整。

②python对大小写敏感,所以None一定要写成None的模样哦。

③二分法的运行时间是O(log n),通常被称作是对数时间;而O(n)通常被称作是线性时间。算法的运行时间通常就是用大O法表示,所以单位肯定不是时间单位啊,因为O里边的n,我们前面已经讲过了,是操作数,即n个动作。相应的,大O法表达的是操作数的增速。

④代码思想:抓住一个基线条件,最小的索引值low≤最大的索引值high,也就是说,保证还有数没有遍历过,必须满足这一条件;抓住一个计算条件,和数组的中位数相比,目标item值是偏大、偏小,还是一致,以此进行迭代或是return操作。

三、选择排序法

选择排序法,顾名思义,就是依次选择最小的,然后大家排队站好,或者依次选择最大的,大家排队站好;也就是说,将无序数列排列成有序数列。这有什么用呢,比如说,如果之后我们需要查询某数值的时候,就可以使用二分法快速查找了,二分法的效率和顺序遍历查询的效率,我们前面已经比较过了哦。在大数据背景下的今天,排序显得异常重要,有拓展想法的朋友,可以去看看数据库索引的相关资料,也是利用排序让检索变得非常便利的一种常用手段。

python实现如下:


选择排序法

几点说明:

①如果是找到最小值,那么可以写得更简单哦:for i in list[:];这边的 i 就不再是截图中的索引的意思了,而是数组中具体值的意思了;不多讲,尝试练习看看结果吧。

②为什么我们采用先找出最小值的索引,而不是最小值呢,因为这样在第二个函数中,我们可以一边删除数组中的最小值,一边讲这个最小值返回,并append到新的数组中去,以上功能用pop就可以简单做到。

pop的作用,就是利用索引值删去对应的值,并将该值返回;如果我们找出的是最小值,那么我们就需要先运行一次findsmallest函数,使新值append到新的数组中去,再运行一次findsmallest以便remove最小值,因为remove函数不提供返回值。

当然,我们也可以只运行一次findsmallest函数,用赋值的方式进行操作,但很明显不如截图里的优雅啊。

③append函数很简单,就是将一个值跟在已有的数组后边就好了。

④代码思想:就是在一堆数中不断抓出最小的数,然后一个个塞进单通道的桶里,排序完成!

那么,下节内容,我将介绍递归思想和它的化身们(含快速排序法),一节课搞懂递归思想,等你一起学习哦!

推荐阅读更多精彩内容