几种常见的排序算法总结
*****选择排序*****
方法描述:首先找到第一个最小的数,和第一个数交换;然后从剩下的找出最小的与第二个交换,以此类推。
效率: 长度为N的数组,大约N2/2比较,N次交换
特点: 1.运行时间和输入无关,有序数组,全部相等的数组,随机数组所用时间一样,没有充分利用输入的初始状态。
2.数据移动最少,与输入成线性关系。
代码:
sort(int[] a){
int n = a.length;
for(int i = 0; i < n; i++){
int min = i;
for(int j = i; j < n; j++){
if(a[j] < a[i])
swap(a[i],a[j]);
}
}
}
*****插入排序*****
背景:打扑克牌
方法描述:后面的数插入到前面已经有序的子数组中
效率: 平均移动一半元素,所以N2/4次比较和交换;最坏N2/2次比较和交换;最好 N-1 次比较和0次交换
特点: 对于部分有序的数组效率非常高,时间是线性级别的。
几个典型的部分有序数组:数组中每个元素距离它的最终位置不远;一个有序的大数组接一个小数组;数组中只有几个元素的位置不正确;
代码:
sort(int[] a){
int n = a.length;
for(int i = 1; i < n; i++){
for(int j = i-1; j >= 0 && a[j] < a[i]; j--){
swap(a[i],a[j]);
}
}
}
*****希尔排序*****
背景:大规模的插入排序效率很低,因为它仅仅交换相邻的元素。希尔排序的思想就是:对大数组中间隔为h的子数组排序,将数组大约分成
h个间隔为h的子数组,并逐渐减小h,h=1时其实就是插入排序。这样做的好处是,排序之初,只对间隔为h的子数组排序,排序规模小,速度快;
随着h的减小,数组趋于有序,此时插入排序能充分发挥作用。
方法描述:后面的数插入到前面已经有序的子数组中
效率: 平均移动一半元素,所以N2/4次比较和交换;最坏N2/2次比较和交换;最好 N-1 次比较和0次交换
特点: 对于部分有序的数组效率非常高,时间是线性级别的。
代码:
sort(int[] a){
int n = a.length;
int h = 1;
while(h < N/3) h = 3*h+1;
while(h >= 1){
for(int i = h; i < n; i++){
for(int j = i; j >= 0 && a[j] < a[i]; j -= h){
swap(a[j],a[i]);
}
}
h = h/3;
}
}
*****归并排序*****
方法描述:归并思想
效率: NlogN
(1)原地归并代码:
public static void merge(int[] a, int lo, int mid, int hi){
int i = lo, j = mid+1;
int[] aux = new int[a.length];
for(int k = lo; k <= hi; k++){
aux[k] = a[k];
}
for(int k = lo; k <= hi; k++){
if(i > mid) a[k] = aux[j++];//异常情况一定要放前面,不然可能导致数组越界,报空指针异常
else if(j > hi) a[k] = aux[i++];
else if(a[i] < a[j]) a[k] = aux[i++];
else a[k] = aux[j++];
}
}
排序:
sort(int[] a, int lo, int hi){
if(hi < lo) return;
int mid = lo + (hi -lo)/2;
sort(a,lo,mid);
sort(a,mid+1,hi);
merge(a,lo,mid,hi);
}
改进:1.小规模用插入排序
2. 测试数组是否有序, a[mid] < a[mid+1] 就认为有序,就跳过merge方法,这样能使得有序的
子数组时间降为线性。
(2)自底向上归并排序:
方法:先两两归并,再44归并,再88,。。。。。
代码:
sort(int[] a){
int n = a.length;
aux = new int[n];
for(int i = 1; i < n; i += i+i){
for(int j = 0; j< n-i; j+= i+i){
merge(a,j,j+i-1,Math.min(j+i+i-1,n-1));
}
}
}
特点:适合链表组织的数据,只需要重新组织链表链接就能将链表原地排序。
*****快速排序*****
特点:原地排序; nlogn; 内循环小,比较次数少,而且是和固定值进行比较,所以非常快. 例如归并,插入排序,都会在内循环中移动元素,所以慢
和归并排序相比:归并是递归在前,处理数组在后;快排相反
代码:
public static void sort(int[] a, int lo,int hi){
if(hi < lo) return;
int j = partition(a,lo,hi);
sort(a,lo,j-1);
sort(a,j+1,hi);
}
static partition(int[] a, int lo, int hi){
int i = lo, j = hi+1;
int guard = a[lo];
while(true){
while(a[++i] < guard) if(i == hi) break;
while(a[--j] > guard) if(j == lo) break;
if(i >= j) break;
swap(a[i],a[j]);
}
swap(a[lo],a[j]);
return j;
}
改进:基于插入排序对于小数组更加高效这一特点。
1. if(hi <= lo) return -> if(hi <= lo+M) {Insertion.sort(a,lo,hi); return;}