假设有个划分函数divide:
divide:将num在[l,r]
范围内,按照nums[l]进行划分,返回一个数组range,划分为:
我们要找第k大的数,可以转化为求第nums.length - k + 1
小的数
对nums在[0,nums.length-1]
范围内做划分,结果为range:
k >= range[0] && k <= range[1]
,说明第k小的数就是nums[range[0]]k < range[0]
,说明第k小的数在前半段,即nums[0,range[0]-1]
中,那就在这个范围内去递归处理k > range[0]
,说明第k小的数在后半段,即nums[range[1]+1,]
中,那就在这个范围内去递归处理最后看看divide怎么实现:
由于最终需要将数组nums在[l,r]范围内划分为4个区域,因此需要定义这3个区域的边界:
小于nums[l]的区域:[l+1,less]
等于nums[l]的区域:[less+1,i-1]
大于nums[l]的区域:[more,r]
[i,more-1]
接着从l+1开始遍历每个元素i,如果
nums[ i ]小于nums[l] :将nums[i]和等于区域的第一个数交换,将小于区域的右边界less++
nums[ i ]大于于nums[l] :将nums[i]和大于区域的前一个数交换,将大于区域的左边界–
什么时候结束循环呢?
当i和more相撞的时候,表示数组中所有的数都遍历过了,且都在正确的位置上
class Solution {
public int findKthLargest(int[] nums, int k) {
k = nums.length - k + 1 -1 ;
int l = 0;
int r = nums.length-1;
while(true) {
int[] range = divide(nums,l,r);
if(k >= range[0] && k <= range[1]) {
return nums[k];
}
if(k < range[0]) {
r = range[0]-1;
} else {
l = range[1] + 1;
}
}
}
private int[] divide(int[] nums,int l,int r) {
if(l == r) {
return new int[]{l,l};
}
int p = nums[l];
// 大于[more,r]
int more = r + 1;
// 小于[l+1,less]
// 等于[less+1,i-1]
int less = l;
int i = l + 1;
for(;i<more;){
if(nums[i] < p){
swap(nums,i,less+1);
i++;
less++;
} else if(nums[i] > p) {
swap(nums,i,more-1);
more--;
} else {
i++;
}
}
// 等于区域 [less+1,i-1]
swap(nums,l,less);
return new int[]{less,i-1};
}
private void swap(int[] nums,int a,int b) {
int temp = nums[a];
nums[a] = nums[b];
nums[b] = temp;
}
}