Kth Largest Element in an Array - 找出数组中的第K大的数

这个问题比较经典,有很多解法,这里介绍几个经典解法。

基于Partition函数

基于快速排序中Partition函数的思想,每次在数组中选择一个数m,把小于m的数放到左边,大于m的数放到右边,看m的位置递归左边或者右边,最后找到第K大的数。

分析此算法的时间复杂度

首先快速排序的平均时间复杂度是O(N*logN),最差情况下(数组有序时)时间复杂度是O(N*N)。
此算法最差情况下也是数组有序时,时间复杂度为O(N*N)。但平均时间复杂度为O(N),因为假设每次丢掉一半的数组,则一共遍历了(1 + 1/2 + 1/4 + …) < 2,所以为O(2N),即为O(N)时间复杂度。
代码:

class Solution{
public:
    int findKthLargest(vector<int>& nums, int k){
        if(k > nums.size())
            return 0;
        return findKthLargest(nums, 0, nums.size() - 1, k);
    }

    int findKthLargest(vector<int>& nums, int begin, int end, int k){
        if(nums.size() <= 0 || k < 1 || begin > end)
            return 0;
        int flag = nums[begin], i = begin, j = end;
        while(i < j){
            while(nums[j] >= flag && i < j)
                j--;
            nums[i] = nums[j];
            while(nums[i] < flag && i < j)
                i++;
            nums[j] = nums[i];
        }
        nums[i] =  flag;
        if(end - i + 1 == k)
            return nums[i];
        else if(end - i + 1 < k) // 留左边
            return findKthLargest(nums, begin, i - 1, k - end + i - 1);
        else  // 留右边
            return findKthLargest(nums, i + 1, end, k);
    }
};

基于堆排序(内存足够存放所有数据)(也可用于Top K)

堆排序的初始建堆操作时间复杂度为O(N)(可证,通过级数),之后每加入新元素或者取走堆顶元素的时间复杂度为O(logN),所以堆排序的时间复杂度为O (N*logN) 。
因为每次取堆顶的元素都是最大/最小的,这里建大顶堆的话,取到第K大的元素时间复杂度为O(N+K*logN),当K不大时,也是比较快的。

class Solution{
public:
    void AdjustHeap(vector<int>& nums, int i, int n){
        // nums[0] 是堆顶,堆中共有n个元素,现在调整 i
        int root = i, j = root * 2 + 1;
        while(j < n){
            if(j + 1 < n && nums[j + 1] > nums[j]) // 对比左右子节点的大小
                j++;
            if(nums[root] < nums[j]){
                swap(nums[root], nums[j]);
                root = j;
                j = root * 2 + 1;
            }
            else
                break;
        }
    }
    int findKthLargest(vector<int>& nums, int k){
        if(k > nums.size())
            return 0;
        // 初始化大顶堆
        for(int i = nums.size() / 2 - 1; i >= 0; --i) // 从 n/2-1 开始
            AdjustHeap(nums, i, nums.size());
        // 对k-1个元素出堆
        for(int i = 0; i < k - 1; ++i){
            nums[0] = nums[nums.size() - 1 - i]; // 把堆中最后一个元素移到堆顶
            AdjustHeap(nums, 0, nums.size() - i);
        }
        return nums[0];
    }
};

基于堆排序(内存不足够存放数据)(不修改原数组)(也可用于Top K)

当数据量特别大,以至于内存中装不下,只能装入硬盘的时候。或者要求不修改原数组时。
这时可以先取K个元素,建一个K个元素的小顶堆,之后每取一个元素就与堆顶的元素对比,小的话就扔掉,大的话就把堆顶元素出堆,把新元素入堆,保证目前遍历的所有元素中前K大的元素都在堆中。遍历结束后,堆顶的元素就是第K大的元素。
这个算法的时间复杂度为O(N*logK)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值