快速排序
快速排序核心思想就是每次在当前区间 [left, right] 中选择出一个元素 nums[p],然后将区间内所有大于它的元素和所有小于它的元素都放到其两侧【具体放在哪一侧取决于是升序还是降序】,然后再递归去处理两侧区间。
class Solution {
public int[] sortArray(int[] nums) {
quickSort2(nums,0,nums.length-1);
return nums;
}
// 荷兰国旗问题
public static int first, last;
public static void quickSort2(int[] arr, int l, int r) {
if(l>=r) return;
// 随机这一下,常数时间比较大
// 但只有这一下随机,才能在概率上把快速排序的时间复杂度收敛到O(n * logn)
int x = arr[l + (int) (Math.random() * (r - l + 1))];
partition2(arr, l, r, x);
// 为了防止底层的递归过程覆盖全局变量
// 这里用临时变量记录first、last
int left = first;
int right = last;
quickSort2(arr, l, left - 1);
quickSort2(arr, right + 1, r);
}
// 已知arr[l....r]范围上一定有x这个值
// 划分数组 <x放左边,==x放中间,>x放右边
// 把全局变量first, last,更新成==x区域的左右边界
public static void partition2(int[] arr, int l, int r, int x) {
first = l;
last = r;
int i = l;
while (i <= last) {
if (arr[i] == x) {
i++;
} else if (arr[i] < x) {
swap(arr, first++, i++);
} else {
swap(arr, i, last--);
}
}
}
public static void swap(int[] nums, int i, int j){
if(i==j) return;
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
双指针交换的方式划分左右区间
将大于切分值的元素都放到左侧,小于切分值得元素都放到右侧。因此我们可以使用双指针分别从两端往中间搜索,分别找到左侧小于切分值的元素和右侧大于切分值的元素,交换之,直到两个指针相遇。
对于这道题,我们可以选择区间的中间值作为切分元素,并且我们要事先将切分值交换到区间的右边界:
- 避免在划分左右区间的时候,将切分值给覆盖掉。
- 最终才可以将切分值放到正确的位置。
class Solution {
public int findKthLargest(int[] nums, int k) {
return quickSortKth(nums,k,0,nums.length-1);
}
private int quickSortKth(int[] nums, int k, int left, int right){
int mid =left + (right-left) / 2;
swap(nums,mid, right); // 将切分值放到右边界避免加入元素的划分
int partition = nums[right], i=left, j=right; // 双指针从左右边界开始,分别找到要交换的元素
while(i<j){
while(i<j && nums[i]>=partition) i++;
while(j>i && nums[j]<=partition) j--; // 找到右侧大于切分值的元素【因为是找大于,即使j从right开始,right也不会被选中】
swap(nums,i,j);
}
swap(nums,i,right); // i最后停留的位置一定是右侧首个小于切分值的元素,与切分值交换,则[left, i)都是大于(等于)切分值,[i+1, right]都是小于(等于)切分值
if(i==k-1) return nums[i];
if(i<k-1) return quickSortKth(nums,k,i+1,right);
return quickSortKth(nums,k,left,i-1);
}
private void swap(int[] nums, int i, int j){
if(i == j) return;
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}