排序
算法的稳定性:排序前的两个元素i和j关键字相等,且i在j的前面,排序后i仍在j的前面。
根据数据元素是否完全在内存中,可将算法分为:
- 内部排序:排序期间元素全部在内存中
- 外部排序:排序期间元素根据要求不断地在内存和外存之间移动
插入排序
基本思想:每次将一个待排序的记录按其关键字大小插入前面已排好序的子序列。
直接插入排序
- 查找出L(i)在L[1....i-1]中的插入位置k
- 将L[k....i-1]所有元素向后移一个位置
- 将L(i)复制到L(k)
初始时L[1]是有序的,之后依次对L(2)~L(n)指向上面的操作,执行n-1次后,就能得到一个有序序列。
空间效率
直接插入排序通常是就地排序,空间复杂度O(1)
时间效率
最好情况,每次插入一个元素都只需比较依次且不需要移动元素,时间复杂度为O(n)
最坏情况,初始元素顺序正好是逆序排列的,时间复杂度为O(n2)
平均时间复杂度为O(n2)
稳定性(√)
适用性
适用于顺序存储和链式存储的线性表。
折半插入排序
基于直接插入排序,在查找插入位置时采用折半查找(二分查找),减少了比较元素的次数,移动元素的次数与直接插入排序相同。
空间效率
O(1)
时间效率
仍然是O(n2)
稳定性(√)
适用性
仅适用于顺序存储的线性表。
直接插入排序与折半插入排序都与数据的初始状态有关,适用于基本有序的排序表且数据量不大。
希尔排序
又称缩小增量排序。
基本思想:把待排序表分割成若干形如L[i , i+d , i+2d , ... , i+kd]的子表,d为增量,对每个子表分别进行插入排序,当整个表中的元素已“基本有序”时,再对全体进行一次直接插入排序。
即先做到局部有序,此时就更加适用于直接插入排序与折半插入排序
空间效率
O(1)
时间效率
依赖于增量序列的函数(数学尚未解决的难题)
n在某个特定范围内,时间复杂度O(n1.3)
最坏情况,时间复杂度O(n2)
稳定性(×)
当相同关键字的记录被划分到不同的子表时,可能会改变它们之间的相对次序。
适用性
仅适用于顺序存储的线性表。
交换排序
基本思想:根据序列中两个关键字的比较结果来对换它们的位置。
冒泡排序
从前往后(或反向)依次两两比较相邻元素的值,若为逆序,则交换它们。
第一次冒泡结果是将最小元素冒泡到第一个位置(或最大元素沉到末尾)。
最多n-1次冒泡,就能得到有序结果。
空间效率
原地O(1)
时间效率
最好情况(初始有序)O(n)
最坏情况(初始逆序)O(n2)
平均时间复杂度O(n2)
稳定性(√)
冒泡排序产生的有序子序列一定是全局有序的(不同与直接插入排序)
有序子序列中的所有关键字一定小于或大于无序子序列中的所有关键字。
快速排序
基于分治法:
- 在L[1....n]中任选一个pivot作为基准(枢轴)(通常取第一个元素)
- 通过一次排序将L分为L[1....k-1]、L[k+1....n]与L(k),其中L(k) = pivot,L[1....k-1]均小于pivot,L[k+1....n]均大于pivot。
- 递归地对L[1....k-1]与L[k+1....n]重复1.和2.操作,直至每个子序列内只有一个元素或为空为止。
空间效率
算法是递归的,需要递归栈空间,取决于栈深度。
最好情况:O(log2n)
最坏情况:O(n)
平均情况:O(log2n)
时间效率
与划分是否对称有关。
最坏情况是两个区域分别包含n-1个元素与0个元素,若每次划分都是这样,时间复杂度最烂O(n2)
为了提高时间效率,尽量选取一个可以将数据中分的pivot(这里说的是关键字大小),如序列的头尾及中间选取3个元素,再取这3个元素的中间值元素作pivot;或者随机的选取pivot。可以使得最坏情况几乎不会发生。
最好情况是每次划分都是最平衡的划分,时间复杂度O(nlog2n)
平均情况的时间复杂度与最好情况的很接近,O(nlog2n)。快速排序是所有内部排序算法中平均性能最优的。
快排中,不产生有序子序列,但每次划分排序后,pivot都会放到最终位置上。
选择排序
基本思想:每一次遍历(如第i次),在待排序元素中选取关键字最小的元素,作为有序子序列中的第i个元素。
简单选择排序
排序表L[1....n],第i次遍历即从L[i....n]中选择关键字最小的元素与L(i)交换,每一次遍历可以确定一个元素的最终位置,n-1遍历即可完成排序。
空间效率
O(1)
时间效率
元素移动的操作次数很少,不会超过3(n-1)次,最好情况是移动0次(表已经有序)。
元素比较的次数与序列的初始状态无关,始终是n(n-1)/2次。
时间复杂度O(n2)
稳定性(×)
第i次遍历,找到第i个小的元素,与位置i的元素交换,如果这两个元素的关键字相同,则会导致相对顺序发生变化。
堆排序
堆的定义:
- L(i) >= L(2i) 且 L(i) >= L(2i+1) 大根堆(大顶堆)
或
- L(i) <= L(2i) 且 L(i) <= L(2i+1) 小根堆(小顶堆)
构造堆(以大根堆为例):
- 对第n/2结点为根的子树
- 若根结点的关键字小于左右孩子关键字较大者,则与较大者交换
- 之后依次对n/2-1 ~ 1为根的子树进行2.中的操作
- 如果1. 2.交换后破坏了子树的堆,则对子树进行2.中的操作

输出栈顶元素后,将堆中最后一个元素放至堆顶,然后进行逐层向下调整。
插入元素时,将新结点放至堆的末尾,然后逐层向上调整。
调整的时间与树高有关,时间复杂度为O(h)。
在建含有n个元素的堆时,关键字的比较总次数不超过4n,时间复杂度为O(n)。
说明可以在线性时间内将无序数组建成一个堆。
空间效率
O(1) (只是在数组内调换了顺序,原地排序)
时间效率
建堆时间为O(n),之后有n-1次向下调整操作,每次调整的时间复杂度为O(h) h = log2n
所以在最好、最坏与平均情况下,时间复杂度O(nlog2n)
稳定性(×)
例如子树中两个关键字相同且都大于根结点,右孩子调整为根结点后,相对次序发生变化。
适用性
堆排序适用于关键字较多的情况。
归并排序
基本思想:假设排序表中有n个记录,将其视为n个有序的子表(每个子表长度为1),然后两两归并,得到n/2个长度为2或1的有序表,继续两两归并,直至合成长度为n的有序表。也称2路归并排序。
归并:
- 需要一个辅助数组,有前后两段相邻的有序表A[low....mid]、A[mid+1....high],将它们都复制到辅助数组B中。
- 每次都从B中对应的两个段中取出两个记录进行关键字比较,小的放入A,然后用该段的下一个记录与刚较大的记录继续比较,直至其中一段记录全部放入A,然后把另一段的剩余记录直接放入A
递归实现(分治思想)
2路归并算法的递归实现:
- 分解。将含有n个元素的排序表分成n/2个元素的子表,采用2路归并排序算法对2个子表递归地进行排序。
- 合并。两个已排序的子表得到排序结果。
空间效率
辅助数组占n个单元,空间复杂度O(n)
时间效率
每次归并的时间复杂度为O(n),共log2n次归并,时间复杂度O(nlog2n)
稳定性(√)
一般而言,对于N个元素的k路归并排序,排序的次数m满足 km = N
基数排序
基本思想:不基于比较和移动,而是基于关键字各位的大小进行排序。是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。
假设长度为n的线性表中每个结点aj的关键字由d元组(kjd-1,kjd-2,....,kj0)组成,满足 0 <= kji <= r-1(0 <= j < n,0 <= i <= d-1)。其中,kjd-1为最主位关键字,kj0为最次位关键字。
多关键字排序的两种方法:
- 最高位优先(MSD):按关键字位权重递减依次逐层划分若干更小的子序列,最后将所有子序列依次连接成一个有序序列。
- 最低位优先(LSD):按关键字位权重递增依次排序,最后形成一个有序序列。


空间效率
辅助存储空间r个队列(r个头指针和尾指针),空间复杂度O(r)
时间效率
需要d次分配和收集,一次分配需要O(n),一次收集需要O(r),时间复杂度O(d(n+r))
与序列的初始状态无关
稳定性(√)
内部排序比较
直接插入排序、冒泡排序、简单选择排序平均时间复杂度都是O(n2)。
其中,冒泡排序、直接插入排序最好可以达到O(n),而简单选择排序与初始状态无关。
希尔排序作为插入排序的拓展,对大规模的排序性能很不错。
快速排序平均达到O(nlog2n),实际应用中常常由于其他算法。
堆排序可在线性时间内完成建堆,在O(nlog2n)内完成排序。
归并排序与快速排序同样基于分治思想,但其分割子序列与初始序列的排列无关,所以最好、最坏与平均都是O(nlog2n)
内部排序算法的应用
需要考虑的因素
- 待排序的元素数量
- 元素本身信息量大小
- 关键字的结构与分布情况
- 稳定性的要求
- 语言工具的条件,存储结构及辅助空间的大小等
- 若元素数量少,可采用直接插入排序或简单选择排序。由于直接插入排序所需的记录移动次数较简单选择排序的多,因此在记录本身信息量较大时,选择简单选择排序好。
- 若初始状态已经基本有序,则选择直接插入排序或冒泡排序好。
- 若元素数量多,应采用O(nlog2n)的算法:快速排序、堆排序或归并排序。其中快速排序是最好的,待排序的关键字随机分布时,快速排序是平均时间最短。堆排序所用的辅助空间小于快速排序,并且不会出现快速排序中的最坏情况。快速排序与堆排序都是不稳定的,如果要求稳定性,则选择归并排序。
- 任何基于比较的排序算法,都至少需要O(nlog2n)的时间。
- 若元素数量多,记录的关键字位数较少且可以分解时,采用基数排序。

浙公网安备 33010602011771号