LeetCode215. 数组中的第K个最大元素 | 刷题打卡

120 阅读2分钟

LeetCode从低效到高效,点击

一、题目描述:

题目要求

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

来源:力扣(LeetCode)链接

示例

输入: [3,2,1,5,6,4]k = 2
输出: 5

二、思路分析:

本题是一道很经典的排序相关的题目,后面很多题都是这个思路。 思路

  1. 排序,访问第k大的数,这种思路没啥好说的 也没啥好写的
  2. 快速排序的每次Partition都可以确定一个位置的值,那么我们只要找到倒数第k个位置的值就可以了,并且可以修改快速排序,不用排完整个顺序,外面套一个二分查找来取快速定位
  3. 使用小根堆,先放进k个数,然后每次放进去都去掉一个小的,这样都扔进去之后的堆顶就是第k大的数

三、AC 代码:

先敲一个快排维持手感。。

// 快速排序 左闭右闭
int Partition(vector<int> &arr,int left,int right){
    // 这里使用第一个值作为起始flag
    int temp = arr[left];
    while(left<right){
        while(left<right && arr[right]>=temp) right--;
        arr[left] = arr[right];
        while(left<right && arr[left]<=temp) left++;
        arr[right] = arr[left];
    }
    arr[left] = temp;
    return  left;
}
void QuickSort(vector<int> &arr,int left,int right){
    if(left<right){
        int mid = Partition(arr,left,right);
        QuickSort(arr,mid+1,right);
        QuickSort(arr,left,mid-1);
    }

}

快速排序+二分查找:

// 48 ms 9.8 MB
// 快速排序用来确定一个元素位置
int Partition(vector<int>& nums,int left,int right){
    // 加上两行随机后性能提升明显,这测试用例坏的很啊 8 ms 9.6 MB
    int keyLoc = left + rand() % (right - left);
    swap(nums[keyLoc], nums[left]);

    int temp = nums[left];
    while(left<right){
        while(left<right&&nums[right]>=temp) right--;
        nums[left] = nums[right];
        while(left<right&&nums[left]<=temp) left++;
        nums[right] = nums[left];
    }
    nums[left] = temp;
    return left;
}

void QuickSortK(vector<int>& nums,int left,int right,int k){
    if(left<right){
        // mid是已经位置确定的元素,这里不用快排都进行排序,使用二分的思路,在乱序数组中用Partition定位中间值
        int mid = Partition(nums,left,right);
        if(mid==k) return;
        else if(mid>k){
            QuickSortK(nums,left,mid-1,k);
        }else{
            QuickSortK(nums,mid+1,right,k);
        }
    }
}

int findKthLargest_v0(vector<int>& nums, int k) {
    int l = nums.size()-1;
    QuickSortK(nums,0,l,l-k+1);
    return nums[nums.size()-k];
}

小根堆:

// 使用小根堆的方式 8 ms 9.9 MB
int findKthLargest(vector<int>& nums, int k) {
    int len = nums.size();
    // 创建小根堆,小根堆中小的在上面,维护一个k大小的小根堆,扔掉更小的,那么小根堆顶就是保存的第k大的数
    priority_queue<int,vector<int>,greater<int>> min_heap;
    int i=0;
    for(;i<k;i++) min_heap.emplace(nums[i]);
    for(;i<len;i++){
        if(nums[i]>min_heap.top()){
            // 这里能确定nums[i]比堆顶大,所以可以先把对顶丢了,再加入nums[i] 这样快一点
            min_heap.pop();
            min_heap.emplace(nums[i]);
           
        }
    }
    return min_heap.top();
}

四、总结:

使用快排+二分的结果与使用小根堆的效果差不多,如果时间紧迫,使用小根堆就可以了。

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情