LeetCode215:Kth Largest Element in an Array

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

For example,
Given [3,2,1,5,6,4] and k = 2, return 5.

Note:
You may assume k is always valid, 1 ≤ k ≤ array’s length.

Credits:
Special thanks to @mithmatt for adding this problem and creating all test cases.

Hide Tags Divide and Conquer Heap

这道题只想出了使用heap来求解的方法,没有想出如何使用分治来求解。但是在Discuss上看到了一份关于使用分治法非常详细的介绍,并且对于其中的分割部分给出了一个非常简洁的代码。


解法一:使用最小堆

这道题既可以使用最小堆也可以使用最大堆来求解,先说使用最小堆的解法。最小堆中最顶端的元素是堆中最小的,我们构建一个大小为k的最小堆,那么最顶端的元素就是第k大的数。

使用数组中的前k个数构成一个最小堆,那么堆顶元素就是第k大的数,然后从第k+1个数开始遍历数组,如果遍历的数组元素小于堆顶元素,舍弃掉,如果遍历的数组元素大于堆顶元素,将堆顶元素出堆,然后将大于堆顶元素的数组中的元素插入堆中,再次形成堆。这样遍历完数组堆顶元素就是第k大的元素,这种解法是保证在遍历到数组的任何一个元素时堆顶元素都是到遍历到的元素为止的第k大的元素。
runtime:8ms

    int findKthLargest(vector<int>& nums, int k) {
        make_heap(nums.begin(),nums.begin()+k,greater<int>());
        for(int i=k;i<nums.size();i++)
        {
            int top=nums.front();
            if(nums[i]>top)
            {
                pop_heap(nums.begin(),nums.begin()+k,greater<int>());
                nums[k-1]=nums[i];
                push_heap(nums.begin(),nums.begin()+k,greater<int>());
            }
        }
        return nums.front();
    }

解法二:使用最大堆

使用最大堆也可以求解,就是将整个数组中的元素构成一个最大堆,这时堆顶元素是最大的,连续将堆顶元素弹出k-1次后堆顶元素就是第k大的数了。由于stl中默认构建的是最大堆,所以这种解法可能会更直观一些。
runtime:8ms

    int findKthLargest(vector<int>& nums, int k) {
        make_heap(nums.begin(), nums.end());
        for (int i=1; i<k; i++){
            pop_heap(nums.begin(), nums.end());
            nums.pop_back();
        }
        return nums[0];
}

解法三:分治法

分治法没有想出来,但是在Discuss中看到了一篇很详细的解答。我将它翻译成了中文。
这种解法是由快速排序发展起来的。
快速排序中,每一次迭代,我们需要选取一个关键元素pivot,然后将数组分割成三个部分:

  1. 小于关键元素pivot的元素
  2. 等于关键元素pivot的元素
  3. 大于关键元素pivot的元素

现在,以[3,2,1,5,4,6]这个数组为例来分析。假定每次选取最左端的元素作为关键的元素pivot,这种情况下,是3,然后我们使用3作为pivot将数组分成上面指定的3个部分,最后结果是[1,2,3,5,4,6]。现在3是第3个元素并且我们知道它也是第3小的元素。

由于上面的分割是将比pivot小的元素放在了pivot的左边,所以pivot当pivot在第k-1位置是是第k小的元素。由于这道题目需要寻找第k大的元素,我们可以修改一下分割过程将比pivot大的元素放在k的左边。这样,分割完成后数组变成了[5,6,4,3,1,2],现在3是第4大的元素,如果我们需要寻找第2大的元素,我们知道它是在3左边,如果我们需要第5大的元素,我们知道它是在3右边。

现在简单写出算法的流程:

  1. 初始化left为0,right为nums.size()-1
  2. 分割数组,如果pivot在第k-1位,返回pivot
  3. 如果pivot在k-1右边,更新right为pivot的位置值
  4. 否则更新left为pivot的位置值。
  5. 重复2的步骤

runtime:8ms

     int findKthLargest(vector<int>& nums, int k) {
         int pivot=nums[0];
         int left=0;
         int right=nums.size()-1;
         while(true)
         {
             int pos=partion(nums,left,right);
             if(pos==k-1)   return nums[pos];
             if(pos<k-1)   left=pos+1;
             else right=pos-1;
         }

     }

     int partion(vector<int> &nums,int begin,int end)
     {
         int left=begin+1;
         int right=end;
         while(left<=right)
         {
             if(nums[left]<nums[begin]&&nums[right]>nums[begin])
                swap(nums[left],nums[right]);
             if(nums[left]>=nums[begin]) left++;
             if(nums[right]<=nums[begin]) right--;
         }
         swap(nums[begin],nums[right]);
         return right;
     }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值