1.插入排序
一般来说,插入排序 都采用in-place在数组上实现。具体算法描述如下:
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果该元素(已排序)大于新元素,将该元素移到下一位置
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置中
- 重复步骤2
如果比较操作 的代价比交换操作 大的话,可以采用二分查找法 来减少比较操作 的数目。该算法可以认为是插入排序 的一个变种,称为二分查找排序 。
上代码:
算法复杂度:
如果目标是把n个元素的序列升序排列,那么采用插入排序 存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1) 次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2 次。插入排序 的赋值操作是比较操作的次数加上(n-1) 次。平均来说插入排序 算法复杂度为O(n 2 )。因而,插入排序 不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千,那么插入排序 还是一个不错的选择。
2.希尔排序
希尔排序(Shell Sort)又叫做缩小增量排序(diminishing increment sort),是一种很优秀的排序法,算法本身不难理解,也很容易实现,而且它的速度很快。
插入排序(Insertion Sort) 的一个重要的特点是,如果原始数据的大部分元素已经排序,那么插入排序的速度很快(因为需要移动的元素很少)。从这个事实我们可以想到,如果原始数据只有很少元素,那么排序的速度也很快。--希尔排序就是基于这两点对插入排序作出了改进。
例如,有100个整数需要排序。
- 第一趟排序先把它分成50组,每组2个整数,分别排序。
- 第二趟排序再把经过第一趟排序后的100个整数分成25组,每组4个整数,分别排序。
- 第三趟排序再把前一次排序后的数分成12组,第组8个整数,分别排序。
- 照这样子分下去,最后一趟分成100组,每组一个整数,这就相当于一次插入排序。
由于开始时每组只有很少整数,所以排序很快。之后每组含有的整数越来越多,但是由于这些数也越来越有序,所以排序速度也很快。
下面用C语言实现希尔排序,用的是K&R里的算法,该算法结构很清晰。
3.快速排序
快速排序使用分治法 (Divide and conquer)策略来把一个序列 (list)分为两个子序列(sub-lists)。
步骤为:
- 从数列中挑出一个元素,称为 "基准"(pivot),
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割之后,该基准是它的最后位置。这个称为分割(partition) 操作。
- 递归 地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
4.堆排序
通常堆积树(heap)是通过一维阵列 来实现的。在起始阵列为 0 的情形中:
- 堆积树的根节点(即堆积树的最大值)存放在阵列位置 1 的地方
注意:不使用位置 0,否则左子树永远为 0 参考
- 节点i的左子节点在位置(2*i)
- 节点i的右子节点在位置(2*i+1)
- 节点i的父节点在位置floor((i-1)/2)
在堆积树的数据结构中,堆积树中的最大值总是位于根节点。堆积树中定义以下几种操作:
- 最大堆积调整(Max_Heapify):将堆积树的末端子结点作调整,使得子结点永远小于父结点
- 建立最大堆积(Build_Max_Heap):将堆积树所有数据重新排序
- 堆积排序(HeapSort):移除位在第一个数据的根结点,并做最大堆积调整的递归 运算
5.归并排序
归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。
归并操作的工作原理如下:
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复步骤3直到某一指针达到序列尾
- 将另一序列剩下的所有元素直接复制到合并序列尾
归并排序具体工作原理如下(假设序列共有n个元素):
- 将序列每相邻两个数字进行归并操作(merge),形成f l o o r (n / 2) 个序列,排序后每个序列包含两个元素
- 将上述序列再次归并,形成f l o o r (n / 4) 个序列,每个序列包含四个元素
- 重复步骤2,直到所有元素排序完毕