算法基础之排序算法[图文]
文章脉络
算法
什么是算法?
算法是解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。 排序算法就是对排序的一种解决方案;而查找算法就是对查找的解决方案。
算法用来干什么?
提高计算机速度并节省存储空间一直成为编程人员努力的方向,排序操作成为程序设计人员考虑的因素之一,排序方法选择得当与否直接影响程序执行的速度和辅助存储空间的占有量,进而影响整个软件的性能。
此处省略2000字……
排序算法
所谓排序就是把一组无序序列按照关键字有序的排列起来。在待排序的数据中,如果存在多个相同关键字,经过排序后这几个相同关键字的相对次序不变,则该排序时稳定的;否则为不稳定的,如下图:
以下不单独说明都以正序排序为例(从小到大)。
插入排序
插入排序的基本思想是从待排序中拿出一个元素,然后逐个拿出一个元素,与当前元素比较,插入到合适的位置,直到全部插入完毕为止。
直接插入排序
直接排序在插入第i个元素时,R1、R2……Ri-1已经排好序,将第i个元素依次与R1到Ri-1比较,找到合适位置。直接排序要进行n-1趟排序。
以数字1-7打乱顺序后排序为例:
希尔排序
希尔排序时对直接插入排序的升级版,我认为希尔排序的升级在于:先利用步长将元素分组,组内排序完毕后;再重新用步长分组,再排序,直到完整排序,实质是分组插入方法。
仍然以被打乱的7个数字为例(相同颜色连线为一组):
选择排序
选择排序的基本思想是从带排序的元素中,选出最小的记录,让后顺次排下去。
直接选择排序
仍然以被打乱的7个数字为例:
堆排序
堆排序的基本思想是先把所有元素按层次遍历放到一棵完全二叉树中,这棵二叉树需要满足:子节点值都大于父节点值(小顶堆),或子节点值都小于父节点值(大顶堆),步骤如下:
- 然后可以知道这棵树中根节点值最小(小顶堆)
- 编号最大的元素与根节点元素交换位置,交换位置后取出原根节点元素
- 重复1、2步骤
仍然以被打乱的7个数字为例:
排出正确大顶堆:
然后让根节点位置与最大编号结点位置对调,并断开原根节点与堆的联系,如下:
重新对剩余的树按大顶堆排序:
重复上面的步骤,得到:
按层次遍历,即可得到排序结果。
从上面也可以看出,按正序排列,需要先构建大顶堆;按逆序排列,需要小顶堆。一位堆排序构建初始堆需要的比较次数较多,所以堆排序不适合元素较少的排序。
交换排序
交换排序的基本思想是两两进行比较,再根据比较结果决定是否交换位置,主要的交换排序有冒泡法和快速排序法。
冒泡排序
恰如其名,冒泡的意思就是假设有两个气泡,重量和待排序中的元素值相同,每次冒泡都让最轻的一个元素向上(前)移动。
仍然以被打乱的7个数字为例:
快速排序
快速排序的基础是分治法,即将问题分解为若干个小但是结构与原问题结构类似的子问题,递归解决这些子问题,再将子问题的解组合为原问题的解。
仍然以被打乱的7个数字为例,为了更清楚显示,我们采用逆序排序:
注意,在选定基准以后,需要左数第几个和右数第几个交替比较:按此例来说,是基准先与右数第一个比较,再与左数第一个比较,再与右数第二个比较……
再在前四个元素和后两个元素中选择基准,再按上述步骤排序:
因为例子比较简单,所以两次就完成排序。快速排序的时间主要是花费在划分操作上,对长度为k的区间进行划分,共需要k-1次关键字比较。它是基于关键字比较的内部算法中最快的一种。
归并排序
归并排序就是使用合并操作完成排序的算法,如果利用二路合并操作,则称为二路合并排序,它的过程是:
- 首先把待排序区间中的每个元素都看作一个有序表通过两两合并,生成n/2(向下取整)个长度为2(最后一个表的长度可能小于2)的有序表,这也称为一趟合并
- 然后再将过n/2(向下取整)个有序表进行两两合并,生成比n/4(向下取整)个长度为4的有序表;
- 如此循环直到得到一个长度为n的有序表,通常需要log2n趟,如果该值为奇数,则为log2n+1(向下取整)。
仍然以被打乱的7个数字为例:
归并排序是稳定的排序,可以使用顺序存储结构也可以使用链表,归并排序需要一个辅助量来暂存两个有序子文件的归并结果。
基数排序
这个排序方法的基本思路上面几种排序稍有区别,根据小学教的,一个3位数字的大小怎么决定?有百位看百位,没百位的看十位,没十位的比个位。这种方法就是基于此的。
这种排序方法,上面用的“打乱的7个数字”就不合适了,重新选择乱序数集合为:{315,707,101,123,489,918,613},因为是正序排列,所以从个位开始比较。
个位排序有什么用,这不还是一点关联也没有吗?开始我也有这个疑问,想着为什么不是从百位开始排,那样更一目了然。现在想想即使按百位开始排,也是把几个百位为某个值的放到一起,仍然不可以一蹴而就,更何况,几个无序数列,不知道一共有多少位。同样,按个位开始排序的方法,就相当于先把个位相同的分为一组,如果再按十位排序,需要遵循已经排好的个位序。
继续按百位排序:
为了减少元素移动次数,队列可以采用链式存储分配,每个队列有两个指针,分别指向队头和队尾。
这个方法思想虽然容易理解,但是因为是从个位开始排,与日常按高位开始排相逆,多少需要思维的转换,大家可以比对从高位开始排序反过来想从个位排。
对比
各种排序方法之间复杂度与稳定性如下表:
上述所讲排序不只是方法,更是思想,例如归并排序:在现在你所处的圈内的值是最高的,如果和其他圈子合并之后呢?呵呵说多了,理解这些排序算法,我觉得最重要的几点是:排序的目的、排序对象、每种排序特有的思想,如果能把算法结合到生活当中,忘是忘不掉的。