0x00 引子 #
排序是很多算法的基础,简简单单的排序前人就归纳出很多种算法,但是这些算法多多少少都有着相同的原理
排序算法有很多,这里我们就简单的谈谈下面7种排序的特点
- 冒泡排序
- 选择排序
- 插入排序
- 希尔排序
- 堆排序
- 归并排序
- 快速排序
0x01 Summary #
从算法的抽象程度上来看,冒泡、选择和插入是比较好理解,我们能用我们生活中的常见事物来理解,后面四种比较抽象,而且相对于前三种平均时间复杂度O(n) = n ^ 2 的来说,后面四种的平均复杂度都比前面的小,尤其是面对大量数据排序来说,后面四种能比前三种跑的更快
0x02 冒泡、选择和插入的特点 #
这三种算法空间复杂度都为O(n) = 1,也就是说在给定一个列表的前提下,无论列表数有多大,额外的排序所需的空间都为常量。但是这三种算法的平均时间复杂度为O(n) = n ^ 2, 也就是说在给定一个长度为n的数组,必须要经历 k × n × n 次操作才能排序,当我们的n比较小的时候,我们无法察觉这个算法与更高效的算法的差别,当n很大的时候,比如一个亿,这时候的要进行的操作就瞬间爆炸了。
这三种算法很大程度上是牺牲了运行时间换取运行空间,我们可以从桶排序上面得到相反的例子,桶排序的时间复杂度为O(n) = 1,空间复杂度为O(n) = n,也就是说在排序上面他的速度是最快的,但是它所花费的空间也是巨大的,有时候时间空间就是两个双刃剑,你如果想节省空间必须浪费时间,你如果想节省时间必须浪费空间
这三种算法原理很简单,而且有一个相同的地方,就是他们每一节排序就会“删掉”一个数字,接下来就是对剩下的排序。当然我这里的删掉就是代表已经排好,然而接下来的过程中不会再涉及到这个数字
这个非常好理解,随便给我们一副牌让一个小朋友把他排出来,小朋友一般就是先找出最大的牌放到最前面,然后在剩下里面找到最大的,依次排下去,最后手里就剩一张牌了,这个牌组就排好了
这三种算法都是基于这个核心,但是具体的算法细节不同。冒泡排序就是先从头到尾依次把最大的交换到最后面;插入排序的话就是我们从第一个数字开始从后面把小的数字插入到前面去;选择的话同冒泡有点相似,不过它并不会把数字传递过去,它直接将未排序的最大值与未排序的末尾值交换。
这三种排序我们都非常好理解,但是他们有一个缺点,就是未排序前必须遍历全部数组,我们都知道现在大数据时代,对于上亿数据执行一次遍历就已经非常耗时间了,为了排一个数字要几乎就遍历一遍(排到后期遍历的越来越少),所以这三种算法在面对巨量数据的时候,花在遍历上面的时间比排序时间要更多。
0x03 希尔和插入排序 #
希尔排序是插入排序的更高效改进方法,说到改进我们就要谈谈插入排序的优缺点
- 优点
我们给定 [ 1, 3, 2, 5, 8 ]
数组,这个数组基本上已经排好序了,如果使用插入排序,我们只要在插入2的时候,将2和3交换就可以,设我们挪动的距离就为1
我们在看这个数组 [ 1, 3, 5, 8, 2 ]
,我们可以看到这里如果使用插入排序,我们会在插入2的时候,要将2依次与8、5、3交换,这样移动的距离就为3
希尔排序改进的地方就是步长,如果它的步长选择的好,它的排序效果越好。这个步长是什么呢,插入排序的步长一直为1,也就是每次遍历的时候步子迈一步,假如步长为2,也就是迈两步。在[ 1, 3, 5, 8, 2 ]
数组中,比如说我们数字2,它在步长为1的时候,它下一步要比较的是8,假如步长为3,那它下一步就直接与3比较了。
所以希尔排序改进就在于他能直接移动多位,在上面的例子里面,步长为3,我们能直接将数组从3的位置移动到2,如果直接使用插入排序,必须移动3次才能达到希尔排序的效果。
希尔排序原理同插入是一样的,不同在于,插入的步长希尔是可变的,这样就为一些“调皮”的数字的移动加快了速度,一步一步的移动他们太累了,直接把步子迈大,一步到位。