參考資料算法
3 1 4 1 5 9 2 6 5 3
1 1 2 3 3 4 5 5 6
下面我就只講解1和2步驟, 而在1,2中,關鍵在於如何實現「劃分」數組
劃分的過程有三個關鍵點:「基準元素」, 「左遊標」 和「右遊標」。數據結構
【注意】這裏你可能會問: 在咱們制定的規則裏, 左遊標先掃描和右遊標先掃描有區別嗎? (若是你這樣想的話就和我想到一塊去了...嘿嘿),由於就上圖而言,兩種狀況下一趟排序中兩個遊標相遇的位置是不一樣的(通常而言,除非相遇位置的下方的元素恰好和基準元素相同):dom
6 1 2 5 4 3 9 7 10 8
1 2 5 4 3 6 9 7 10 8
// 交換兩個數組元素 private static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; }
private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j爲左右掃描指針 PS: 思考下爲何j比i 多加一個1呢? int pivotkey = a[low]; // pivotkey 爲選取的基準元素(頭元素) while(true) { while (a[--j]>pivotkey) { if(j == low) break; } // 右遊標左移 while(a[++i]<pivotkey) { if(i == high) break; } // 左遊標右移 if(i>=j) break; // 左右遊標相遇時候中止, 因此跳出外部while循環 else exchange(a,i, j) ; // 左右遊標未相遇時中止, 交換各自所指元素,循環繼續 } exchange(a, low, j); // 基準元素和遊標相遇時所指元素交換,爲最後一次交換 return j; // 一趟排序完成, 返回基準元素位置 }
private static void sort (int [] a, int low, int high) { if(high<= low) { return; } // 終止遞歸 int j = partition(a, low, high); // 調用partition進行切分 sort(a, low, j-1); // 對上一輪排序(切分)時,基準元素左邊的子數組進行遞歸 sort(a, j+1, high); // 對上一輪排序(切分)時,基準元素右邊的子數組進行遞歸 }
while (a[--j]>pivotkey) { ... }
先將右遊標左移一位,而後判斷指向的數組元素和基準元素pivotkey比較大小, 若是該元素大於基準元素, 那麼循環繼續,j再次減1,右遊標再次左移一位...... (循環體能夠看做是空的)函數
if(i>=j) break;
從i < j到 i == j 表明了「遊標未相遇」到「遊標相遇」的過分過程,此時跳出外部循環, 切分已接近完成,緊接着經過 exchange(a, low, j) 交換基準元素和相遇遊標所指元素的位置, low是基準元素的位置(頭部元素), j是當前兩個遊標相遇的位置性能
while (a[--j]>pivotkey) { if(j == low) break; } // 右遊標左移
中,當隨着右遊標左移,到j = low + 1的時候,有 a[--j] == pivotkey爲true(二者都是基準元素),自動跳出了while循環,因此就不須要在循環體裏再判斷 j == low 了測試
int i = low, j = high+1
結合下面兩個While循環中的判斷條件:優化
while (a[--j]>pivotkey) { ... } while (a[++i]<pivotkey) { ... }
可知道, 左遊標 i 第一次自增的時候, 跳過了對基準元素 a[low] 所執行的 a[low] < pivotkey判斷, 這是由於在咱們當前的算法方案裏,基準元素和左遊標初始所指的元素是同一個,因此沒有執行a[low]>pivotke這個判斷的必要。因此跳過( 一開始a[low] == pivotkey,若是執行判斷那麼一開始就會跳出內While循環,這顯然不是咱們但願看到的)ui
sort(a, low, j-1); sort(a, j+1, high);
進行下一輪遞歸,設置j -1 和j + 1 是由於上一輪基準元素的位置已是有序的了,不要再歸入下一輪遞歸裏編碼
public class QuickSort { // 交換兩個數組元素 private static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j爲左右掃描指針 PS: 思考下爲何j比i 多加一個1呢? int pivotkey = a[low]; // pivotkey 爲選取的基準元素(頭元素) while(true) { while (a[--j]>pivotkey) { if(j == low) break; } // 右遊標左移 while(a[++i]<pivotkey) { if(i == high) break; } // 左遊標右移 if(i>=j) break; // 左右遊標相遇時候中止, 因此跳出外部while循環 else exchange(a,i, j) ; // 左右遊標未相遇時中止, 交換各自所指元素,循環繼續 } exchange(a, low, j); // 基準元素和遊標相遇時所指元素交換,爲最後一次交換 return j; // 一趟排序完成, 返回基準元素位置 } private static void sort (int [] a, int low, int high) { if(high<= low) { return; } // 當high == low, 此時已經是單元素子數組,天然有序, 故終止遞歸 int j = partition(a, low, high); // 調用partition進行切分 sort(a, low, j-1); // 對上一輪排序(切分)時,基準元素左邊的子數組進行遞歸 sort(a, j+1, high); // 對上一輪排序(切分)時,基準元素右邊的子數組進行遞歸 } public static void sort (int [] a){ //sort函數重載, 只向外暴露一個數組參數 sort(a, 0, a.length - 1); } }
public class Test { public static void main (String [] args) { int [] array = {4,1,5,9,2,6,5,6,1,8,0,7 }; QuickSort.sort(array); for (int i = 0; i < array.length; i++) { System.out.print(array[i]); } } }
01124556789
if(high<= low) { return; }
if(high<= low + M) { Insertion.sort(a,low, high) return; } // Insertion表示一個插入排序類
private static void sort (int [] a, int low, int high) { if(high<= low + 10) { Insertion.sort(a,low, high) return; } // Insertion表示一個插入排序類 int j = partition(a, low, high); // 調用partition進行切分 sort(a, low, j-1); // 對上一輪排序(切分)時,基準元素左邊的子數組進行遞歸 sort(a, j+1, high); // 對上一輪排序(切分)時,基準元素右邊的子數組進行遞歸 }
public static void sort (int [] a){ StdRandom.shuffle(a) // 外部導入的亂序算法,打亂數組的分佈 sort(a, 0, a.length - 1); }
private static int getRandom (int []a, int low, int high) { int RdIndex = (int) (low + Math.random()* (high - low)); // 隨機取出其中一個數組元素的下標 exchange(a, RdIndex, low); // 將其和最左邊的元素互換 return a[low]; } private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j爲左右掃描指針 PS: 思考下爲何j比i 多加一個1呢? int pivotkey = getRandom (a, low, high); // 基準元素隨機化 while(true) { while (a[--j]>pivotkey) { if(j == low) break; } // 右遊標左移 while(a[++i]<pivotkey) { if(i == high) break; } // 左遊標右移 if(i>=j) break; // 左右遊標相遇時候中止, 因此跳出外部while循環 else exchange(a,i, j) ; // 左右遊標未相遇時中止, 交換各自所指元素,循環繼續 } exchange(a, low, j); // 基準元素和遊標相遇時所指元素交換,爲最後一次交換 return j; // 一趟排序完成, 返回基準元素位置 }
package mypackage; public class QuickSort { // 交換兩個數組元素 private static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } // 選取左中右三個元素,求出中位數, 放入數組最左邊的a[low]中 private static int selectMiddleOfThree(int[] a, int low, int high) { int middle = low + (high - low)/2; // 取得位於數組中間的元素middle
if(a[low]>a[high]) { exchange(a, low, high); //此時有 a[low] < a[high] } if(a[middle]>a[high]){ exchange(a, middle, high); //此時有 a[low], a[middle] < a[high] } if(a[middle]>a[low]) { exchange(a, middle, low); //此時有a[middle]< a[low] < a[high] } return a[low]; // a[low]的值已經被換成三數中的中位數, 將其返回 } private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j爲左右掃描指針 PS: 思考下爲何j比i 多加一個1呢? int pivotkey = selectMiddleOfThree( a, low, high); while(true) { while (a[--j]>pivotkey) { if(j == low) break; } // 右遊標左移 while(a[++i]<pivotkey) { if(i == high) break; } // 左遊標右移 if(i>=j) break; // 左右遊標相遇時候中止, 因此跳出外部while循環 else exchange(a,i, j) ; // 左右遊標未相遇時中止, 交換各自所指元素,循環繼續 } exchange(a, low, j); // 基準元素和遊標相遇時所指元素交換,爲最後一次交換 return j; // 一趟排序完成, 返回基準元素位置 } private static void sort (int [] a, int low, int high) { if(high<= low) { return; } // 當high == low, 此時已經是單元素子數組,天然有序, 故終止遞歸 int j = partition(a, low, high); // 調用partition進行切分 sort(a, low, j-1); // 對上一輪排序(切分)時,基準元素左邊的子數組進行遞歸 sort(a, j+1, high); // 對上一輪排序(切分)時,基準元素右邊的子數組進行遞歸 } public static void sort (int [] a){ //sort函數重載, 只向外暴露一個數組參數 sort(a, 0, a.length - 1); } }
while(a[++i]<pivotkey) { if(i == high) break; }
咱們只要嘗試把這一做用交給a[++i]<pivotkey去完成, 不就能夠把 if(i == high) break; 給去掉了嗎?
public class QuickSort { // 交換兩個數組元素 private static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } //將原數組裏最大的元素移到最右邊, 構造「哨兵」 private static void Max(int [] a) { int max = 0; for(int i = 1; i<a.length;i++) { if(a[i]>a[max]) { max = i; } } exchange(a, max, a.length -1); } private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j爲左右掃描指針 PS: 思考下爲何j比i 多加一個1呢? int pivotkey = a[low]; // pivotkey 爲選取的基準元素(頭元素) while(true) { while (a[--j]>pivotkey) { } // 空的循環體 while(a[++i]<pivotkey) { } // 空的循環體 if(i>=j) break; // 左右遊標相遇時候中止, 因此跳出外部while循環 else exchange(a,i, j) ; // 左右遊標未相遇時中止, 交換各自所指元素,循環繼續 } exchange(a, low, j); // 基準元素和遊標相遇時所指元素交換,爲最後一次交換 return j; // 一趟排序完成, 返回基準元素位置 } private static void sort (int [] a, int low, int high) { if(high<= low) { return; } // 當high == low, 此時已經是單元素子數組,天然有序, 故終止遞歸 int j = partition(a, low, high); // 調用partition進行切分 sort(a, low, j-1); // 對上一輪排序(切分)時,基準元素左邊的子數組進行遞歸 sort(a, j+1, high); // 對上一輪排序(切分)時,基準元素右邊的子數組進行遞歸 } public static void sort (int [] a){ //sort函數重載, 只向外暴露一個數組參數 Max(a); // 將原數組裏最大元素移到最右邊, 構造「哨兵」 sort(a, 0, a.length - 1); } }
package mypackage; public class Quick3way { public static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } public static void sort (int [] a, int low, int high) { if(low>=high) { return; } int lt = low, gt = high, i =low+1; int v = a[low]; while(i<=gt) { int aValue = a[i]; if(aValue>v) { exchange(a, i, gt--); } else if(aValue<v) { exchange(a, i++, lt++); } else{ i++; } } sort(a, low, lt-1); sort(a, gt+1, high); } public static void sort (int [] a) { sort(a, 0, a.length - 1); } }