標籤:rand str ati 不一致 math com ret lin 實現
普通快速排序
找一個基準值base,然後一趟排序後讓base左邊的數都小於base,base右邊的數都大於等於base。再分為兩個子數組的排序。如此遞迴下去。
public class QuickSort { public static <T extends Comparable<? super T>> void sort(T[] arr) { sort(arr, 0, arr.length - 1); } public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right) { if (left >= right) return; int p = partition(arr, left, right); sort(arr, left, p - 1); sort(arr, p + 1, right); } private static <T extends Comparable<? super T>> int partition(T[] arr, int left, int right) { T base = arr[left]; int j = left; for (int i = left + 1; i <= right; i++) { if (base.compareTo(arr[i]) > 0) { j++; swap(arr, j, i); } } swap(arr, left, j); return j;//返回一躺排序後基準值的下角標 } public static void swap(Object[] arr, int i, int j) { if (i != j) { Object temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } private static void printArr(Object[] arr) { for (Object o : arr) { System.out.print(o); System.out.print("\t"); } System.out.println(); } public static void main(String args[]) { Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6}; printArr(arr);//3 5 1 7 2 9 8 0 4 6 sort(arr); printArr(arr);//0 1 2 3 4 5 6 7 8 9 }}
快速排序最佳化:
在數組幾乎有序時,快排效能不好(因為每趟排序後,左右兩個子遞迴規模相差懸殊,大的那部分最後很可能會達到O(n^2))。
解決:基準值隨機地選取,而不是每次都取第一個數。這樣就不會受“幾乎有序的數組”的幹擾了。但是對“幾乎亂序的數組”的排序效能可能會稍微下降,至少多了排序前交換的那部分,亂序時這個交換沒有意義...有很多“運氣”成分..
public class QuickSort { public static <T extends Comparable<? super T>> void sort(T[] arr) { sort(arr, 0, arr.length - 1); } public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right) { if (left >= right) return; int p = partition(arr, left, right); sort(arr, left, p - 1); sort(arr, p + 1, right); } private static <T extends Comparable<? super T>> int partition(T[] arr, int left, int right) { //排序前,先讓基準值和隨機的一個數進行交換。這樣,基準值就有隨機性。 //就不至於在數組相對有序時,導致左右兩邊的遞迴規模不一致,產生最壞時間複雜度 swap(arr,left,(int)(Math.random()*(right - left + 1)+left)); T base = arr[left]; int j = left; for (int i = left + 1; i <= right; i++) { if (base.compareTo(arr[i]) > 0) { j++; swap(arr, j, i); } } swap(arr, left, j); return j;//返回一躺排序後,基準值的下角標 } public static void swap(Object[] arr, int i, int j) { if (i != j) { Object temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } private static void printArr(Object[] arr) { for (Object o : arr) { System.out.print(o); System.out.print("\t"); } System.out.println(); } public static void main(String args[]) { Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6}; printArr(arr);//3 5 1 7 2 9 8 0 4 6 sort(arr); printArr(arr);//0 1 2 3 4 5 6 7 8 9 }}
快速排序繼續最佳化:
快排是不斷減小問題規模來解決子問題的,需要不斷遞迴。但是遞迴到規模足夠小時,如果繼續採用這種 不穩定+遞迴 的方式執行下去,效率不見得會很好。
所以當問題規模較小時,近乎有序時,插入排序表現的很好。Java內建的Arrays.sort()裡經常能看到這樣的注釋:“Use insertion sort on tiny arrays”,“Insertion sort on smallest arrays”
public class QuickSort { public static <T extends Comparable<? super T>> void sort(T[] arr) { sort(arr, 0, arr.length - 1, 16); } /** * @param arr 待排序的數組 * @param left 左閉 * @param right 右閉 * @param k 當快排遞迴到子問題的規模 <= k 時,採用插入排序最佳化 * @param <T> 泛型,待排序可比較類型 */ public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right, int k) { // 規模小時採用插入排序 if (right - left <= k) { insertionSort(arr, left, right); return; } int p = partition(arr, left, right); sort(arr, left, p - 1, k); sort(arr, p + 1, right, k); } public static <T extends Comparable<? super T>> void insertionSort(T[] arr, int l, int r) { for (int i = l + 1; i <= r; i++) { T cur = arr[i]; int j = i - 1; for (; j >= 0 && cur.compareTo(arr[j]) < 0; j--) { arr[j + 1] = arr[j]; } arr[j + 1] = cur; } } private static <T extends Comparable<? super T>> int partition(T[] arr, int left, int right) { //排序前,先讓基準值和隨機的一個數進行交換。這樣,基準值就有隨機性。 //就不至於在數組相對有序時,導致左右兩邊的遞迴規模不一致,產生最壞時間複雜度 swap(arr, left, (int) (Math.random() * (right - left + 1) + left)); T base = arr[left]; int j = left; for (int i = left + 1; i <= right; i++) { if (base.compareTo(arr[i]) > 0) { j++; swap(arr, j, i); } } swap(arr, left, j); return j;//返回一躺排序後,基準值的下角標 } public static void swap(Object[] arr, int i, int j) { if (i != j) { Object temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } private static void printArr(Object[] arr) { for (Object o : arr) { System.out.print(o); System.out.print("\t"); } System.out.println(); } public static void main(String args[]) { Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6}; printArr(arr);//3 5 1 7 2 9 8 0 4 6 sort(arr); printArr(arr);//0 1 2 3 4 5 6 7 8 9 }}
快速排序及最佳化(Java實現)