魔改快速排序
首先来看两种写法
先来看官方的
class Solution {
private final static Random random = new Random(System.currentTimeMillis());
public int findKthLargest(int[] nums, int k) {
int n = nums.length;
return partition(nums, 0, n - 1, n - k);
}
private int partition(int[] nums, int left, int right, int targe) {
if (left == right) {
return nums[targe];
}
int randomIndex = left + random.nextInt(right - left + 1);
swap(nums, left, randomIndex);
int pivot = nums[left], i = left - 1, j = right + 1;
while (i < j) {
do {
i++;
} while (nums[i] < pivot);
do {
j--;
} while (nums[j] > pivot);
if (i < j) {
swap(nums, i, j);
}
}
if (targe <= j) {
return partition(nums, left, j, targe);
} else {
return partition(nums, j + 1, right, targe);
}
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
接着是熟悉的快排传统写法
class Solution {
private final static Random random = new Random(System.currentTimeMillis());
public int findKthLargest(int[] nums, int k) {
int n = nums.length;
return partition(nums, 0, n - 1, n - k);
}
private int partition(int[] nums, int left, int right, int targe) {
if (left == right) {
return nums[targe];
}
int randomIndex = left + random.nextInt(right - left + 1);
swap(nums, left, randomIndex);
int pivot = nums[left], i = left, j = right;
while (i < j) {
while (i < j && nums[j] >= pivot)
--j;
nums[i] = nums[j];
while (i < j && nums[i] <= pivot)
++i;
nums[j] = nums[i];
}
nums[i] = pivot;
if (targe <= j) {
return partition(nums, left, j, targe);
} else {
return partition(nums, j + 1, right, targe);
}
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
其中,官方的快排只需要4ms(使用了随机pivot),而传统的写法需要上千毫秒,而两者仅仅是while(i < j)中的写法不同而已,真是神奇
那么就以官方写法为准
算法思路
在快速排序中,每一轮遍历都会确定一个pivot的最终位置。如下图所示,第一轮确定了3的最终位置
如果确定的pivot的位置,刚好是我们需要找的K呢?也就是len - k,那么是不是就找到了答案。要注意的是,整个算法执行完,数组可能还是未排序完全的,但我们只需要关注pivot的位置是否等于len - k,那么怎么确定什么时候找到len - k呢?
我们可以使用两个指针left和right来作为每次递归的边界,再用两个指针i和j作为工作指针,i = left - 1, j = right + 1,设len - k为target。
如果一轮遍历下来,j在target的左边,那么下一轮递归将left设为j + 1,right不变;
如果一轮遍历下来,j在target的右边,或者target == j,那么下一轮递归将right设为j,left不变。
这样做都是为了使left和right向target逼近,俗称夹逼法。。。
当left == right,说明找到了target。
注意边界条件nums[i] < pivot和nums[j] > pivot中都不是<=和>=,是为了使pivot可以得到交换