排序算法根据是否需要访问外存,分为内部排序和外部排序。
内部排序是指待排序列完全存放在内存中所进行的排序过程,适合不太大的元素序列。
外部排序是指大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,来达到排序整个文件的目的。
一.内部排序
使用内存,分为插入(直接插入排序 希尔排序)、选择(简单选择排序 堆排序)、交换(冒泡排序 快速排序)、归并、基数。
7.归并排序
分治思想
- 基本思想:将两个有序的数组归并到另一个数组中,需要开辟额外的空间。
- 代码实现(Java):
public static void mergeSort(int[] arr) {
if(arr==null || arr.length<2) {
return ;
}
mergeSort(arr,0,arr.length-1);
}
public static void mergeSort(int[] arr,int left,int right) {
if(left==right) {
return ;
}
int mid=left+((right-left)>>1);
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
merge(arr,left,mid,right);
}
public static void merge(int[] arr,int left,int mid,int right) {
int[] temp=new int[right-left+1];
int i=0;
int p1=left;
int p2=mid+1;
while(p1<=mid && p2<=right) {
temp[i++]=arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
}
while(p1<=mid) {
temp[i++]=arr[p1++];
}
while(p2<=right) {
temp[i++]=arr[p2++];
}
for(i=0;i<temp.length;i++) {
arr[left++]=temp[i];
}
}
- 算法分析:时间复杂度为O(nlogn),是稳定排序。
8.基数排序
基数排序基于桶排序,是分配排序的实现。分配排序的基本思想是排序过程无需比较关键值,通过分配和收集来实现排序。
桶排序的基本思想是:设置若干个桶,依次扫描待排序的元素,把关键字在某个范围内的元素全部装到第K个桶中(分配),然后按序号依次将各个非空的桶首尾连接起来(收集)。
桶的类型设计成链表,先进先出原则。桶排序的时间复杂度为O(m+n)。基数排序是对桶排序的一种改进。
- 基本思想:对数字型或字符型的单关键字,可以看作由多个数位或字符构成的多关键字,此时可以采用“分配-收集”的方法进行排序,这一过程称为基数排序法,其中每个数字或字符可能的取值个数称为基数。即将待排数据中的每组关键字依次进行桶排序。
- 代码实现(Java):
- 算法分析:时间复杂度为O(n)(O(d * (n + r)),d 为位数,r 为基数,n 为原数组个数)。空间复杂度较高,且待排序的元素都要在一定的范围内。
9.总结
排序算法的复杂度和稳定性:
算法的稳定性是指排序前后,相等的两个元素次序不变。
选择算法时要考虑的因素:待排序元素的数目、记录其他元素的内存大小、元素的结构及分布情况、对排序稳定性的要求。
设待排元素的大小为n,
- 当n较大时,采用时间复杂度为O(nlogn)的算法:快速、堆、归并
- 当n较大,内存空间无要求且要求稳定性时,选择归并排序
- 当n较小时,可采用直接插入或直接选择排序:直接插入(元素分布有序)、直接选择(元素分布有序且不要求稳定性)
- 一般不使用冒泡排序
- 基数排序是稳定的,但存在一定的局限性:元素可分解,较适用于数字的排序且最好是无符号、数字较密集的序列。
- 元素的移动次数与关键字的初始排列次序无关的是,基数排序
- 元素的比较次数与初始序列无关的是,选择排序
- 算法的时间复杂度与初始序列无关的是,选择排序、归并排序、堆排序
- 不稳定的排序:快选希堆