目录
内部排序的分类
1.插入排序
1.1直接插入排序
思想:每次将一个待排序的记录 按照关键字大小插入到已排好序的子序列中(每次排序好序列呈现局部有序)
空间效率:仅使用常数个辅助单元,空间复杂度O(1)
时间效率:向子序表中逐个插入元素的操作进行n-1趟每次操作都分为比较关键字和移动元素,比较次数和移动次数与初始状态有关
最好情况:有序每次插入只需要比较不需要移动,复杂度为O(n)
最坏情况:顺序相反(逆序)总的比较和移动次数最大,复杂度为O(n^2)
平均情况:总的比较和移动次数约为n^4/4
稳定性:稳定
适用性;适用于顺序存储和链式存储的线性表
1.2折半插入排序
思想:将比较和移动操作分离,即先折半查找出元素的待插入位置,然后统一的移动待插入位置之后的所有元素
时间效率:比较次数时间复杂度:O(nlog2n),比较次数与初始状态无关,移动次数时间复杂度:O(n^2)移动次数与初始状态有关
稳定性:稳定, 适用于数据量较小的排序表
适用性;适用于顺序存储的线性表
1.3希尔排序
思想:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止(通过间隔分为一组对这组的所有数进行排序,每次分组增量减少)
空间效率:仅使用常数个辅助单元,空间复杂度O(1)
当增量为1时,希尔排序就是直接插入排序
最坏情况:复杂度为O(n^2)
稳定性:不稳定
适用性;适用于顺序存储线性表
2.交换排序
2.1冒泡排序
思想:从后往前(从前往后)两两比较相邻的元素值,若为逆序(A[i-1]>A[i]),则交换它们,直到序列比较完(产生的有序子序列一定是全局有序)
空间效率:仅使用常数个辅助单元,空间复杂度O(1)
时间效率:初始序列有序时,比较次数为(n-1),移动次数为0,逆序是需要进行n-1趟排序,第i趟排序要进行n-i次关键字比较,每次移动元素3次,即比较次数=n(n-1)/2,移动次数=3n(n-1)/2
最好情况:复杂度为O(n)
最坏情况:复杂度为O(n^2)
平均情况:复杂度为O(n^2)
稳定性:稳定
适用性;适用于顺序存储和链式存储的线性表
2.2快速排序
思想:基本思想是基于分治法:在待排续表L[1...n]中任取一个元素pivot作为基准(通常取首元素),通过一趟排序将待排序表划分为独立的两个部分L[1....k-1]和L[k+1...n],使得L[1....k-1]中所有元素小于pivot,使得L[k+1...n]中所有元素大于或等于pivot,则pivot放在最终的位置L(k)上,则完成一次划分
空间效率:快速排序是递归,需要借助一个递归工作栈来保存每一层递归调用的信息,其容量与递归调用的最大一层一致。最好的情况O(log2n),最坏的情况:要进行n-1次递归调用,因此栈的深度为O(n),平均情况,栈的深度为O(log2n)
时间效率:快排的最坏情况发生在两个区域分别包含n-1个元素和0个元素时,对应于初始排序表基本有序或基本逆序是,最坏的时间复杂度为O(n^2)
提高算法效率:①尽量选择头尾及中间选取三个元素,再取三个元素的平均值作为基准,②或者随机选择表中的元素,这样最坏的情况不会发生
最好情况:时间复杂度为O(nlog2n)
快排是所有内部排序算法中平均性能最优的排序算法
稳定性:不稳定
适用性;适用于顺序存储的线性表
3.选择排序
3.1堆排序
思想:将堆视作一颗完全二叉树分为大根堆和小根堆,首先将存在L[1...n]中的n个元素建成初始堆,因为堆本身的特点(以大根堆为例),所以堆顶元素就是最大值
大根堆:L(i)>=L(2i)且L(i)>=L(2i+1)(根>=左右)
小根堆:L(i)<=L(2i)且L(i)<=L(2i+1)(根<=左右)(l<=i<=向下取整(n/2))
空间效率:仅使用常数个辅助单元,空间复杂度O(1)
时间效率:建堆时间为O(n),之后有n-1次向下调整操作,没次调整的时间复杂度为O(h)
最好情况:O(nlog2n)
最坏情况:O(nlog2n)
平均情况:O(nlog2n)
稳定性:不稳定
适用性;适用于顺序存储的线性表
3.2简单选择排序
思想:假设排序表为L[1...n],第i趟排序从L[i...n]中选择关键字最小的元素于L(i)交换,每一趟排序可以确定一个元素的最终位置,这样经过n-1趟排序就可以使得整个排序表有序
空间效率:仅使用常数个辅助单元,空间复杂度O(1)
时间效率:元素移动次数不超过3(n-1)次,如表已经有序;当元素间比较次数于序列的初始状态无关,始终是n(n-1)/2次,时间复杂度为O(n^2)
最好情况:移动0次
稳定性:不稳定
适用性;适用于顺序存储和链式存储的线性表
4.归并排序、基数排序、计数排序
4.1归并排序
思想:将两个或两个以上的有序表合并成一个新的有序表
空间效率:辅助空间刚好为n个单元,空间复杂度为O(n)
时间效率:每趟归并的时间复杂度为O(n),共需要进行[log2n]趟归并,算法的时间复杂度为O(nlog2n)
m路归并:每选出一个元素需要对比关键字m-1次
稳定性:稳定
适用性;适用于顺序存储和链式存储的线性表
4.2基数排序
思想:基于位数的大小排序
空间效率:一趟排序需要的辅助空间为r(r个队列:r个队头指针和r个队尾指针),会重复适用这些队列,空间复杂度为O(r)
时间效率:基数排序需要进行d趟分配和收集操作。一趟分配需要遍历所有关键字,时间复杂度为O(n);一趟收集需要合并r个队列,时间复杂度为O(r)。因此时间复杂度为O(d(n+r)),与初始状态无关
稳定性:稳定
适用性;适用于顺序存储和链式存储的线性表
4.3计数排序
思想:对每个待排序元素x,统计小于x的元素个数,利用该信息就可确定x的最终位置
空间效率:输出数组的长度为n;辅助的计数数组的长度为k,空间复杂度为O(n+k),若不把输出的数组视为辅助空间,则空间复杂度为O(k)
时间效率:时间复杂度为O(n+k),当k=O(n),时间复杂度为O(n);当k>O(nlogn),效率不如一些基于比较的排序(快排、堆排序)
稳定性:稳定
适用性;适用于顺序存储的线性表,序列中元素是整数且元素范围(0~k-1)不能太大,否则会浪费辅助空间
各种排序算法的性质
算法种类 | 时间复杂度 | 空间复杂度 | 是否稳定 | ||
---|---|---|---|---|---|
最好 | 平均 | 最坏 | |||
直接插入排序 | O(n) | O(n^2) | O(n^2) | O(1) | 是 |
冒泡排序 | O(n) | O(n^2) | O(n^2) | O(1) | 是 |
简单选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 否 |
希尔排序 | 否 | ||||
快速排序 | O(nlog2n) | O(nlog2n) | O(n^2) | O(nlog2n) | 否 |
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 否 |
二路归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 是 |
基数排序 | O(d(n+r)) | O(d(n+r)) | O(d(n+r)) | O(r) | 是 |
从适用性上看:适合顺序存储(折半插入排序、希尔排序、快速排序、堆排序),适合顺序存储和链表存储(直接插入排序、简单选择排序、归并排序、冒泡排序、基数排序)
选择排序算法需要考虑的因素
(1)待排序的元素个数n
(2)待排序的元素初始状态
(3)关键字的机构及其分布情况
(4)稳定性的要求
(5)存储结构即辅助空间的大小限制
排序算法的小结
(1)若n较小,可采用直接插入排序或简单选择排序。由于直接插入排序所需的记录移动次数较简单选择排序的多,因此当记录本身信息量较大时,用简单选择排序较好。
(2) 若n较大,应采用时间复杂度为0(nlog2n)的排序算法:快速排序、堆排序或归并排序。当待排序的关键字随机分布时,快速排序被认为是目前基于比较的内部排序算法中最好的算法。堆排序所需的辅助空间少于快速排序,且不会出现快速排序可能的最坏情况,这两种排序都是不稳定的。若要求稳定且时间复杂度为O(nlog2n),可选用归并排序。
(3)若文件的初始状态已按关键字基本有序,则选用直接插入或冒泡排序为宜。
(4)在基于比较的排序算法中,每次比较两个关键字的大小之后,仅出现两种可能的转移,因此可以用一棵二叉树来描述比较判定过程,由此可以证正明:当文件的n个关键字随机分布时,任何借助于“比较”的排序算法,至少需要0(nlog2n)的时间。
(5)若n很大,记录的关键字位数较少且可以分解时,采用基数排序较好。
(6)当记录本身信息量较大时,为避免耗费大量时间移动记录录,可用链表作为存储结构。