一、算法原理
基於分治的思想,是冒泡排序的改進型。首先在數組中選擇一個基准點(該基准點的選取可能影響快速排序的效率,后面講解選取的方法),然后分別從數組的兩端掃描數組,設兩個指示標志(low指向起始位置,high指向末尾),首先從后半部分開始,如果發現有元素比該基准點的值小,就交換low和high位置的值,然后從前半部分開始掃秒,發現有元素大於基准點的值,就交換low和hi位置的值,如此往復循環,直到low>=high,然后把基准點的值放到hi這個位置。一次排序就完成了。以后采用遞歸的方式分別對前半部分和后半部分排序,當前半部分和后半部分均有序時該數組就自然有序了。
二、算法舉例
三、算法實現
1 /** 2 * 查找出中軸(默認是最低位low)的在numbers數組排序后所在位置 3 * 4 * @param array 待查找數組 5 * @param lo 開始位置 6 * @param hi 結束位置 7 * @return 中軸所在位置 8 */ 9 static int getMiddle(int []array,int lo,int hi) { 10 //固定的切分方式 11 int key=array[lo]; 12 while(lo<hi){ 13 //從后半部分向前掃描 14 while(array[hi]>=key&&hi>lo){ 15 hi--; 16 } 17 array[lo]=array[hi]; 18 //從前半部分向后掃描 19 while(array[lo]<=key&&hi>lo){ 20 lo++; 21 } 22 array[hi]=array[lo]; 23 } 24 array[hi]=key; 25 return hi; 26 } 27 28 /** 29 * 30 * @param numbers 帶排序數組 31 * @param low 開始位置 32 * @param high 結束位置 33 */ 34 static int[] quickSort(int[] numbers,int low,int high) { 35 if(low < high) { 36 int middle = getMiddle(numbers,low,high); //將numbers數組進行一分為二 37 quickSort(numbers, low, middle-1); //對低字段表進行遞歸排序 38 quickSort(numbers, middle+1, high); //對高字段表進行遞歸排序 39 } 40 return numbers; 41 }
快速排序的時間復雜度為O(NlogN)。
四、算法優化
對於基准位置的選取一般有三種方法:固定切分,隨機切分和三取樣切分。固定切分的效率並不是太好,隨機切分是常用的一種切分,效率比較高,最壞情況下時間復雜度有可能為O(N2).對於三數取中選擇基准點是最理想的一種。
1 /** 2 * 3 * @param array 待排序的數組 4 * @param lo 開始位置 5 * @param hi 結束位置 6 * @return 基准值所在位置 7 */ 8 static int partition(int []array,int lo,int hi){ 9 //三數取中 10 int mid=lo+(hi-lo)/2; 11 if(array[mid]>array[hi]){ 12 swap(array[mid],array[hi]); 13 } 14 if(array[lo]>array[hi]){ 15 swap(array[lo],array[hi]); 16 } 17 if(array[mid]>array[lo]){ 18 swap(array[mid],array[lo]); 19 } 20 int key=array[lo]; 21 22 while(lo<hi){ 23 while(array[hi]>=key&&hi>lo){ 24 hi--; 25 } 26 array[lo]=array[hi]; 27 while(array[lo]<=key&&hi>lo){ 28 lo++; 29 } 30 array[hi]=array[lo]; 31 } 32 array[hi]=key; 33 return hi; 34 } 35 36 /** 37 * 交換a,b的值 38 * @param a 待交換a 39 * @param b 待交換b 40 */ 41 static void swap(int a,int b){ 42 int temp=a; 43 a=b; 44 b=temp; 45 } 46 47 /** 48 * 49 * @param array 帶排序數組 50 * @param lo 開始位置 51 * @param hi 結束位置 52 */ 53 static int[] sort(int[] array,int lo ,int hi){ 54 if(lo>=hi){ 55 return array; 56 } 57 int index=partition(array,lo,hi); 58 sort(array,lo,index-1); 59 sort(array,index+1,hi); 60 return array; 61 }
快速排序在序列中元素很少時,效率將比較低,不然插入排序,因此一般在序列中元素很少時使用插入排序,這樣可以提高整體效率。
測試用例:
1 package recursion; 2 3 import java.util.Arrays; 4 5 /** 6 * @author zsh 7 * @company wlgzs 8 * @create 2019-02-17 15:32 9 * @Describe 快速排序 10 */ 11 public class QuickSort { 12 13 /** 14 * 查找出中軸(默認是最低位low)的在numbers數組排序后所在位置 15 * 16 * @param array 待查找數組 17 * @param lo 開始位置 18 * @param hi 結束位置 19 * @return 中軸所在位置 20 */ 21 static int getMiddle(int []array,int lo,int hi) { 22 //固定的切分方式 23 int key=array[lo]; 24 while(lo<hi){ 25 //從后半部分向前掃描 26 while(array[hi]>=key&&hi>lo){ 27 hi--; 28 } 29 array[lo]=array[hi]; 30 //從前半部分向后掃描 31 while(array[lo]<=key&&hi>lo){ 32 lo++; 33 } 34 array[hi]=array[lo]; 35 } 36 array[hi]=key; 37 return hi; 38 } 39 40 /** 41 * 42 * @param numbers 帶排序數組 43 * @param low 開始位置 44 * @param high 結束位置 45 */ 46 static int[] quickSort(int[] numbers,int low,int high) { 47 if(low < high) { 48 int middle = getMiddle(numbers,low,high); //將numbers數組進行一分為二 49 quickSort(numbers, low, middle-1); //對低字段表進行遞歸排序 50 quickSort(numbers, middle+1, high); //對高字段表進行遞歸排序 51 } 52 return numbers; 53 } 54 55 /** 56 * 57 * @param array 待排序的數組 58 * @param lo 開始位置 59 * @param hi 結束位置 60 * @return 基准值所在位置 61 */ 62 static int partition(int []array,int lo,int hi){ 63 //三數取中 64 int mid=lo+(hi-lo)/2; 65 if(array[mid]>array[hi]){ 66 swap(array[mid],array[hi]); 67 } 68 if(array[lo]>array[hi]){ 69 swap(array[lo],array[hi]); 70 } 71 if(array[mid]>array[lo]){ 72 swap(array[mid],array[lo]); 73 } 74 int key=array[lo]; 75 76 while(lo<hi){ 77 while(array[hi]>=key&&hi>lo){ 78 hi--; 79 } 80 array[lo]=array[hi]; 81 while(array[lo]<=key&&hi>lo){ 82 lo++; 83 } 84 array[hi]=array[lo]; 85 } 86 array[hi]=key; 87 return hi; 88 } 89 90 /** 91 * 交換a,b的值 92 * @param a 待交換a 93 * @param b 待交換b 94 */ 95 static void swap(int a,int b){ 96 int temp=a; 97 a=b; 98 b=temp; 99 } 100 101 /** 102 * 103 * @param array 帶排序數組 104 * @param lo 開始位置 105 * @param hi 結束位置 106 */ 107 static int[] sort(int[] array,int lo ,int hi){ 108 if(lo>=hi){ 109 return array; 110 } 111 int index=partition(array,lo,hi); 112 sort(array,lo,index-1); 113 sort(array,index+1,hi); 114 return array; 115 } 116 117 /** 118 * insertSort(arr,k) 遞歸實現插入排序 119 * 找重復:insertSort(arr,k-1) 將k-1個排序后,把arr[k]插入到前面的數據中 --子問題 120 * 找變化:變化的量應該作為參數 k。 121 * 找邊界:出口 終止的條件 k == 0 122 */ 123 static int[] insertSort(int[] arr,int k){ 124 if (k == 0){ 125 return arr; 126 } 127 //對前k-1個元素排序 128 insertSort(arr,k-1); 129 //把k位置上的元素插入到前面的部分 130 int x = arr[k]; 131 int index = k -1; 132 while (index >= 0 && x <arr[index]){ 133 arr[index+1] = arr[index]; 134 index--; 135 } 136 arr[index+1] = x; 137 return arr; 138 } 139 140 /** 141 * 優化后的快速排序算法 142 * @param array 待排序數組 143 * @param lo 開始位置 144 * @param hi 結束位置 145 * @return 已排序的數組 146 */ 147 static int[] quick(int []array ,int lo,int hi){ 148 if(hi-lo+1<10){ 149 return insertSort(array,array.length-1); 150 }else{ 151 return sort(array,lo,hi); 152 } 153 } 154 155 public static void main(String[] args) { 156 int[] arr = new int[]{6,3,7,4,1,5,8,9,5,44,6,5}; 157 System.out.println(Arrays.toString(quickSort(arr,0,arr.length-1))); 158 System.out.println(Arrays.toString(sort(arr,0,arr.length-1))); 159 System.out.println(Arrays.toString(quick(arr,0,arr.length-1))); 160 } 161 }