选择:1、外循环i,范围N-1,最后一位必为最小。
2、对于N大小,需要N次比较。
3、运行时间和输入的有序无序随机都无关,因为必须遍历
4 、数据移动是最少。
5、优化是堆排序
// public static void SelectSort(Comparable[] a) {
// int N = a.length;
// int mins;
// for (int i = 0; i < N - 1; i++) {
// mins = i;
// for (int j = i + 1; j < N; j++) {
//
// if (SortUtils.less(a[j], a[mins])) {
// mins = j;
// }
// SortUtils.exch(a, i, mins);
// }
// }
// }
插入排序 :1、一般是N2/4比较和交换,最坏是N2/2,最好是N-1比较和0次交换
2、排有序会比随机和逆序数快得多
3、小数组经常是用插入,会比递归快,例如快排和归并
// public static void insertsort(Comparable [] a ){
// int n = a.length;
//
// for (int i = 1; i < n; i++) {
// for (int j = i; j >0 && SortUtils.less(a[j], a[j-1]); j--) {
// SortUtils.exch(a, j, j-1);
// }
// }
希尔: 1、插入的优化
2、通过h有序数据,避免了如果主键最小的元素正好在数组尽头,那么插入排序效率不高的情况
3、递增序列 h= 3h+1,测试中效率最高
4、通过while循环实现h,再通过h>=1的循环来控制分组,由h=h/3来减少分组距离
// public static void shellsort(Comparable[] a ){
//
// int n = a.length;
// int h1 = 1 ;
// while (h1<n/3) {
// h1 = 3*h1+1;
//
//
// }
// while (h1>=1) {
// for (int i = h1; i < n; i++) {
//
// for (int j = i+1; j >=h1 && SortUtils.less(a[j],a[j-h1]); j=j-h1) {
// SortUtils.exch(a, j, j-h);
// }
// }
// h1=h1/3;
//
// }
// }
归并:
1、分治思想
2、对小规模数据 N=15左右,使用插入排序,测试快10%-15%
3、测试数据是否已经有序,就是a[mid]<a[mid+1]就是有序,跳出merge方法
4、使用arraycope不讲元素复制到辅助数组。底层jni。并且辅助数组aux不声明在merge中为局部变量,因为会减低效率
5、4个条件判断:左边板用尽(取右半边的元素)、右半边用尽(取左边板的元素)、右半边的当前元素小于左半边的当前元素(取右半边的元素)、右半边的当前元素大于左边的当前元素(取半边)
6、原地归并的循环K,是循环c【】a数组,i=lo,j=mid+1,是aux数组辅助a数组
public static void ms(Comparable[] a) {
Comparable[] aux1 = a.clone();
ms(a, aux1, 0, a.length - 1);
}
private static void ms(Comparable[] a, Comparable[] aux1, int lo, int hi) {
if (lo>=hi+10) {
Insertion.sort(a);
return;
}
int mid = lo + (hi - lo) / 2;
ms(a, aux1, lo, mid);
ms(a, aux1, mid + 1, hi);
if (!SortUtils.less(aux1[mid + 1], aux1[mid])) {
System.arraycopy(aux1, lo, a, lo, hi - lo + 1);
return;
}
m(a, aux1, lo,mid, hi);
}
private static void m(Comparable[] a, Comparable[] aux1, int lo, int mid,
int hi) {
int j = mid +1;
int i = lo;
for (int k = lo; k < hi; k++) {
if (i>mid) {
a[k]=aux1[j++];
}else if (j>hi) {
a[k]=aux1[i++];
}else if (SortUtils.less(aux1[j], aux1[i])) {
a[k]=aux1[j++];
}else {
a[k]=aux1[i++];
}
}
}
快排:
1、选择基准:在待排序列中,按照某种方式挑出一个元素,作为 "基准"
2、分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大
3、递归地对两个序列进行快速排序,直到序列为空或者只有一个元素。
4、三次取中+插排+聚合相等元素+尾递归 +(多核心多线程) stl sort 两者效率差不多
private static int partition(Comparable[] a, int lo, int hi) {
int i = lo;
int j = hi + 1;
Comparable v = a[lo];
while (true) {
while (SortUtils.less(a[++i], v)) {
if (i == hi)
break;
}
while (SortUtils.less(v, a[--j])) {
if (j == lo)
break;
}
if (i >= j) {
break;
}
SortUtils.exch(a, i, j);
}
SortUtils.exch(a, j, lo);
return j;
}
public static void QuickSort(Comparable[] a, int lo, int hi) {
if (hi <= lo + 10) {
Insertion.sort(a);
return;
}
int k = partition(a, lo, hi);
QuickSort(a, lo, k - 1);
QuickSort(a, k + 1, hi);
}
堆排序:
1、10亿中取10个最大
2、从a【1】使用,跳去a【0】,在exch和less中,减一
3、for循环中sink构造有序堆,while循环中,sink用来排序
4、sink中的while,2j<=h,先是两子节点比较,右边一定小于左边,像一个退役的老人,跟新晋比较,一步一步下滑,swim则是上浮
5、而不使用swim是因为效率
6、先下沉后上浮。字符串或者其他兼职较长进行排序
7、无法利用缓存
8、是我们所知的唯一能够同时最有地利用空间和时间的方法。最坏也是 2NlgN的比较
// public static void hs(Comparable[] a) {
// int N = a.length;
// for (int i = N / 2; i > 0; i++) {
// sink1(a, i, N);
// }
// while (N > 1) {
// exch(a, 1, N--);
// sink(a, 1, N);
// }
// }
//
// private static void sink1(Comparable[] a, int i, int n) {
// while(2*i<n){
// int h = 2*i;
// if (h<n && less(a, h, h+1)) {
// h++;
// }if (!less(a,i,h)) {
// break;
// }
// exch(a,i,h);
// i=h;
// }
// }