##目录
##初级排序 ###一、 选择排序简述:选择排序就是遍历一遍数组把最小的和第一个数字交换。第二遍遍历数组时候选择和第二个交换,一次类推。
//注意不要在for循环中用a.length()不然每次都要获取a.length();
public voiv sort(Comparable[] a){
//for(int i=0,i<a.length;i++){
int N = a.length;
for(int i=0,i<N;i++){
int min = i;
for(int j=i+1;j<N;j++){
//if(a[min]>a[j]){
if(less(a[j],a[min])){
min = j;
}
//int tmp = a[i];
//a[i] = a[min];
//a[min] = tmp;
exch(a,j,min);
}
}
}
二、 插入排序
简述:类似于打牌时候边抽牌边整理。第二张开始,就最大的开始对比,然后直到不大时候插入。
public void sort(Comparable[] a){
int N = a.length;
//下面是我写的,不知道书上有什么优势。有空测一下。
//for(int i=1;i<N;i++){
// if(less(a[i],a[i-1])){
/// int pos = i;
// while(less(a[pos],a[pos-1])&&pos>0){
// exch(a,pos,pos-1);
// pos--;
// }
// }
//}
for(int i=1,i<N;i++){
for(int j=i;j>0&&less(a[j],a[j-1]);j--){
exch(a,j,j-1);
}
}
}
命题C
插入排序的比较次数 大于等于倒置的数量,小于等于倒置数量加上数组大小再减一。
最坏就是全倒置嘛,这时候每倒置一次就要多比一次,然而数组里每个数都要比较,除了第一个数时候和自身不比较,所以是 最差等于倒置数量+数组大小-1。
所以对 部分有序的数组十分高效,也适合小规模数组。
当不考虑交换和比较成本,两个差不多,但是感觉数组规模小的化插入有优势,规模大的化选择有优势。【平均而言】
三、希尔排序
public static void sort(Comparable a){
int N = a.length;
int h = 1;
//
while(h<N/3){
h = h * 3 +1
}
while(h>=1){
for(int i=h;i<N;i++){
for(int j=i;j<N&&less(a[j],a[j-h]);j-=h){
exch(a,j,j-h);
}
}
h=h/3;
}
}
平均每个比较次数增幅为N1/5 大的话才比较明显。
没什么可说的,三方比较,希尔排序一直赛高。可以把希尔排序看作套壳的插入排序。
归并排序
最基础先是合并两个有序表的方法[书上叫 原地归并抽象方法,不知道为甚么]
//谨记读数组时候要用copy数组,改写的时候才用原数组
public static void merge(Comparable[] a,int lo,int mid,int hi){
int N = a.length;
//copy
for(int k = lo;k <= hi;k++){
auk[k]=a[k];
}
int i = lo;
int j = mid + 1;
for(int k=0;k<N;k++){
if(i>mid) a[k] = auk[j++];
else if(j>=hi) a[k] = auk[i++];
else if(less(aux[j],aux[i])) a[k] = auk[i++];
else a[k] = auk[i++];
}
}
接下来是使用到原地归并的两个归并方法,使用思想不一样,但是我觉得本质都一样。
我测试了 两个排序50个100万完全随机数据,花的时间都差不多。
前者说是自顶向下,其实也是下到两两元素才开始正式比对。
自顶向下的归并排序 分治思想
private static Comparable[] aux;
public static void sort(Comparable[] a){
int N = a.length;
aux = new Comparable(N);
sort(a,0,N-1);
}
public static void sort(Comparable[] a,int lo ,int hi){
int mid = lo + (hi - lo)/2;
if(lo>=hi) return;
sort(a,lo,mid);
sort(a,mid+1,hi);
merge(a,lo,mid,hi);
}
因为插入排序适合小数组,据说是 归并 + 插入 赛高。还没有验证。
自底向上的归并排序
private static Compararble[] aux;
public static void sort(Comparable[] a){
int N = a.length;
//分割,1 ,2 ,4 ,6 ,8
//边界问题最头疼,首先为什么是N 不是N - 1?
//最重要的一点是 我门用sz表达的是分割的数组数字个数,如果是个数自然是按原值N过来。
for(int sz=1;sz < N ;sz = sz + sz){
//这里为什么是N
//暂时不知道,但是我用-1试过好几次也没差。反正就是为了防止过界又防止余下部分太小
for(int lo = 0;lo < N - sz;lo +=2*sz){
merge(a,lo,lo + sz -1 ,Math.min(lo + 2*sz - 1,N - 1));
}
}
}
最后的几个结论不是很懂,只能二刷时候再看了。
快速排序
快速排序也是把问题分开再分开,不同的是它并不是平均分成两组,而是产生一个分开因子。笼统而言和归并差不多吧,但是我测试速度感觉比归并快一点。时间能达到1/2甚至更多
public static void sort(Comparable[] a){
int N = a.length;
sort(a,0,N-1);
}
public static void sort(Comparable[] a,int lo,int hi){
if(lo>=hi) return;
int par = partition(a,lo,hi);
sort(a,lo,par - 1);
sort(a,par + 1 ,hi);
}
//core code
public static void partition(Comparable[] a, int lo,int hi){
Comparable v = a[lo];
i = lo;
j = hi+1;
while(true){
//while(less(a[++i],v)) if(i>=hi) break;
while(less(a[++i],v)) if(i==hi) break;
//while(less(v,a[--j])) if(j<=lo) break;
//因为a[lo] = v; 所以 当j = lo时候根本进不来,所以if(j==lo)是个多余的条件
while(less(v,a[--j])) {}/*if(j==lo) break;*/
if(i>=j) break;
exch(a,i,j);
}
exch(a,j,lo);
return j;
}
//quick3way
public static void sort3way(Comparable[] a,int lo,int hi){
if(lo>=hi) return;
int lt = lo;
int gt = hi;
int i = lo + 1;
Comparable v = a[lo];
//其实就是用 i 去遍历每一个数字,小的从 lt 插入 大的 从 gt插入。此为三向切分
while(i<=gt){
int cmp = a[i].compareTo(v);
if(cmp<0) exch(a,lt++,i++);
//换了之后i不用往下走,因为gt已经把一个陌生值换给它了。
else if(cmp>0) exch(a,i,gt--);
//和自己相等,下一个
else i++;
}
//lt-1 都是比v小的
//gt+1 都是比v大的
//剩下的都是等于v的。
sort3way(a,lo,lt-1);
sort3way(a,gt+1,hi);
}
###优先队列 优先队列是一种思想,只能说堆排序用了这种思想。 大概就是当一个完全二叉树时候,对于一个节点k(从 1 开始的序号), 他的父节点为k/2 左子节点为 2k 右子节点为 2k + 1 下面的节点总比上面的节点大(根据实际用途,自己要求) 我们可以通过遍历某个节点的 当我们添加一个方法的时候 可以先将插入对象添加到队尾 可以使用 swim方法,让其上浮 ####由下至上的对有序化(上浮) ``` //注意我们这里假设数组从 index 1 开始 public void swim(Comparable[] a,int k){ //k为插入元素index while(k>=1){ if(less(a[k/2],a[k])){ exch(a,k/2,k); k = k/2; } } //这样直接上浮到适合的位置为止。 } ``` 书上介绍优先队列用法,就是用来快速删除最大(最小【需另设队列】)元素的。
当我们删除最大节点,先使他下沉,使他子元素补位,
由上至下的堆有序化,下沉
//注意我们这里假设数组从 index 1 开始
public void sink(Comparable[] a,int k,int N){
while(k<=N){
int j = 2 * k;
//让哪个儿子重哪个儿子上
if(j<N && less(a[j],a[j+1])) j=j+1;
if(less(a[j],a[k])) break;
exch(a,k,j);
//换做最大元素,下一回合。
k = j;
}
}
堆排序
public static void sort(Comparable[] a){
//第一步 排成优先队列
int N = a.length;
//元素先按1开始,等到使用数组的时候,再恢复实际元素
for(int k=N/2;k>=1;k--){
sink(a,k,N);
}
while(N>1){
//最前面肯定使最大的,所以将其和正确位置置换
exch(a,0,N-1);
//最先面的已经不是最轻的了,换人。
sink(a,1,--N);
}
}
public static void sink(Comparable[] a,int k,int N){
while(2*k<=N){
int j = 2 * k;
//使用时候按实际位置所以要-1.包括less 和 exch方法
if(j < N && less(a[j-1],a[j])) j = j + 1;
//如果不比儿子轻,就此结束,下一回合。
if(!less(a[k-1],a[j-1])) break;
exch(a,j-1,k-1);
k = j;
}
}