一、算法原理
基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点(该基准点的选取可能影响快速排序的效率,后面讲解选取的方法),然后分别从数组的两端扫描数组,设两个指示标志(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 }