##目錄
##初級排序 ###一、 選擇排序簡述:選擇排序就是遍歷一遍數組把最小的和第一個數字交換。第二遍遍歷數組時候選擇和第二個交換,一次類推。
//注意不要在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;
}
}