top k问题
从n个数中找到第k大的元素
一、快速排序法
在快速排序中,需要找到一个枢轴元素,并调整数组元素,使得枢轴元素左侧全为小于它的数,右侧全是大于它的数。如果在某次划分后枢轴元素的位置正好为倒数第k个数,那么此时枢轴元素就是要找的第k大的数。如果划分后枢轴元素的下标小于目标下标,则只需要递归对右子区间进行划分;如果划分后枢轴元素的下标大于目标下标,则只需要递归对左子区间进行划分。
时间复杂度:O(n)
int partition(vector<int>& nums,int low,int high){
//划分为两个子区间
int zhu=nums[low];//主元
int i=low;int j=high;
while (j>i)
{
while (i<j&&nums[j]>=zhu)
{
j--;
}
nums[i]=nums[j];
while (i<j&&nums[i]<=zhu)
{
i++;
}
nums[j]=nums[i];
}
nums[j]=zhu;
return j;
}
int kc(vector<int>& nums,int low,int high,int k){
int q=partition(nums,low,high);
if (q==k)
{
return nums[q];
}
if (q<k)
{
return kc(nums,q+1,high,k);
}
else
return kc(nums,low,q-1,k);
}
int findKthLargest(vector<int>& nums, int k) {
//快速排序方法
int l=nums.size();
return kc(nums,0,l-1,l-k);
}
二、堆排序法
首先建立一个大根堆,然后进行k-1次堆顶元素的删除,堆顶元素就是第k大的元素了。
时间复杂度为 O((n + k)log n)
void Headadjust(int A[],int k,int len){
//调整以k为根的子树为大根堆
//保存要下坠的元素
A[0]=A[k];
for (int i = 2*k; i <= len; i*=2)
{ //i指向最大孩子
if (i<len&&A[i]<A[i+1])
{
i++;
}
if (A[i]<=A[0])
{//不需要再下坠
break;
}else{
A[k]=A[i];
k=i;
}
}
A[k]=A[0];
}
void swaps(int &a,int &b){
int temp=a;
a=b;
b=temp;
}
int findKthLargest(vector<int>& nums, int k) {
int l=nums.size();
int A[l+1];
for (int i = 1; i <= l; i++)
{
A[i]=nums[i-1];
}
//先建立大根堆
for (int i = l/2; i > 0; i--)
{
Headadjust(A,i,l);
}
//进行堆排序
for (int i = 0; i < k-1; i++)
{
swaps(A[1],A[l-i]);//将堆顶元素与堆末尾元素互换
Headadjust(A,1,l-i-1);
}
return A[1];
}