一、快排的引入:
1.给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。
这就是快排的一个partition的过程,简单,代码就不给了。
2.稍微进阶:荷兰国旗问题:给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边
解决这个问题,可以对经典快排进行优化,因为划分为等于num的数的区域不用在比较了,减少了比较次数
1 public static int[] partition(int[] arr, int l, int r, int p) { 2 int less = l - 1; 3 int more = r + 1; 4 while (l < more) { 5 if (arr[l] < p) { 6 swap(arr, ++less, l++); 7 } else if (arr[l] > p) { 8 swap(arr, --more, l); 9 } else { 10 l++; 11 } 12 } 13 return new int[] { less + 1, more - 1 }; 14 }
二、快排:基于上述荷兰国旗问题实现
1 public static void quickSort(int[] arr) { 2 if (arr == null || arr.length < 2) { 3 return; 4 } 5 quickSort(arr, 0, arr.length - 1); 6 } 7 8 public static void quickSort(int[] arr, int l, int r) { 9 if (l < r) { 10 swap(arr, l + (int) (Math.random() * (r - l + 1)), r); 11 int[] p = partition(arr, l, r); 12 quickSort(arr, l, p[0] - 1); 13 quickSort(arr, p[1] + 1, r); 14 } 15 } 16 17 public static int[] partition(int[] arr, int l, int r) { 18 int less = l - 1; 19 int more = r; 20 while (l < more) { 21 if (arr[l] < arr[r]) { 22 swap(arr, ++less, l++); 23 } else if (arr[l] > arr[r]) { 24 swap(arr, --more, l); 25 } else { 26 l++; 27 } 28 } 29 swap(arr, more, r); 30 return new int[] { less + 1, more }; 31 } 32 33 public static void swap(int[] arr, int i, int j) { 34 int tmp = arr[i]; 35 arr[i] = arr[j]; 36 arr[j] = tmp; 37 }
三.快排的应用
1.top k问题
用经典快排每次partition返回的值index,当index == k - 1,这左边(或右边)就是top k最小(或最大)值,可以达到O(n)的时间复杂度
1 public int findKth(int[] p,int L,int R,int k){ 2 if(L > R || k < 1)//检查输入参数是否合法 3 return -1; 4 if(L == R)//如果L等于R说明已找到,直接返回 5 return p[R]; 6 int temp = quickSort(p,L,R);//进行一次快排,返回下标 7 if(k+L == temp+1)//如果k+L等于返回的下标加1(L不一定从0开始) 8 return p[temp];//则直接返回 9 if(k+L < temp+1)//如果k+L小于返回的下标加1 10 return findKth(p,L,temp-1,k);//在temp的左边查找第k大数 11 else//否则,在temp的右边部分查找第k-(temp-L+1)大数。这里,右边的第 12 //k-(temp-L+1)大数就是整个数组的第k大数 13 return findKth(p,temp+1,R,k-(temp-L+1)); 14 } 15 /* 16 * 一次快速排序 17 *以p[L]为比较对象,比p[L]大或等于的在其左边,否则在其右边 18 */ 19 public int quickSort(int[] p ,int L,int R){ 20 if(L >= R) 21 return -1; 22 int i = L; 23 int j = R; 24 int temp = p[L]; 25 while(i < j){ 26 while(i < j && p[j] < temp) j--; 27 if(i < j){ 28 p[i] = p[j]; 29 i++; 30 } 31 while(i < j && p[i] > temp) i++; 32 if(i < j){ 33 p[j] = p[i]; 34 j--; 35 } 36 } 37 p[i] = temp; 38 //去掉以下两句注释,再将return注释掉,并且将返回值改为void, 39 //就是一个完整的快速排序 40 //quickSort(p ,L,i-1); 41 //quickSort(p ,i+1,R); 42 return i; 43 }
当然还有一些题:1.对字符数组排序,所有的小写字母在前,大写字母在后
2.所有的负数在前,正数在后,0在中间等等这类通过某种属性进行划分为两路或三路的过程,都可以基于partition的过程