前置知识:算法讲解023 随即快速排序
无序数组中求第K大或者第K小的数字,需要寻找的是数组排序后第K大的数字,而不是第K个不同的数字。要求:时间复杂度O(n)
利用改写快排的方法,时间复杂度O(n),额外空间复杂度O(1)。
上面问题的解法就是随机选择算法,是常考内容!证明见算法导论9.2.
算法导论第九章,还有一个BFPTR算法,不用随机选择一个数的方式,时间复杂度也能到O(n),额外空间复杂度O(logn),这个算法很冷门,知道有即可。
我们利用改写快排方法:我们假设一个数组,要求第52大的数字,那么荷兰国旗问题划分之后我们看中间等于部分,下标有没有命中52,如果有,就说明我们随机选择的这个数字就是第52的数。
如果没有,那么是大了还是小了?这样我们就从双边变成了单边,这就是改写快排方法。
快排是O(nlogn),能满足我们这里要求的O(n)吗?
最好情况:选择的数字刚好处于中间位置,每次划分都能平均分左右,这样时间复杂度是n+n/2+n/4+...+n/2^n
最坏情况:要求第0大的数字,但是每次我都选择了第100大,第99大...这样就是O(n^2)。
这样概率的期望就是O(n)。
习题:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
class Solution {
public int findKthLargest(int[] nums, int k) {
return randomizedSelect(nums,nums.length-k);
}
//如果对arr排序的话,第i位置的数字应该是什么?
public int randomizedSelect(int[]arr,int i){
int l=0;
int r=arr.length-1;
int ans=0;
while(l<=r){
int x=arr[l+(int)(Math.random()*(r-l+1))];
partition(arr,l,r,x);
if(i<first){
r=first-1;
}
else if(i>last){
l=last+1;
}
else{
ans=arr[i];
break;
}
}
return ans;
}
public void partition(int[]arr,int l,int r,int temp){
first=l;
last=r;
int i=l;
while(i<=last){
if(arr[i]==temp){
i++;
}
else if(arr[i]<temp){
swap(arr,first,i);
first++;
i++;
}
else{
swap(arr,last,i);
last--;
}
}
}
public int last,first;
public void swap(int[]arr,int a,int b){
int temp=arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
}