快速排序——215. 数组中的第K个最大元素

题目描述:

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

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

示例 1:

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

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

提示:

1 <= k <= nums.length <= 104
-104 <= nums[i] <= 104

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:

        快排数组后返回对应元素,关键是手写快排,我已经快疯了。。。。之前每次写快排,都不怎么能写对,尤其是有没有等号的问题,以及是否交换的问题,以及选取基准的问题,以及数组中有没有重复元素的问题。今天折腾了一上午,算是搞明白一点了,但还是记住这个模版吧:

        1.基准选数组第一个元素。如果使用随机数进行选择,也请把随机数选择的元素交换到数组头部。

        2.

while(left<right&&nums[right]>=target) right--;
while(left<right&&nums[left]<=target) left++;

        这两个循环语句中,必须带等号!!!

        3.每一轮快排结束后,把基准值nums[key]和快排结束位置的值nums[left]进行交换,即:swap(nums[key],nums[left]);

        4.数组中无论是否有重复元素,这一套模版都能使用

代码如下:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 快排数组,并取值
        quicksort(nums,0,nums.size()-1);
        return nums[nums.size()-k];
    }
    void quicksort(vector<int>& nums,int begin,int end)
    {
        if(begin>=end) return;
        int left=begin;
        int right=end;

        // 获取随机数,来选取随机基准
        // int x=begin+rand()%(end-begin+1);
        // swap(nums[left],nums[x]);

        // 选取数组第一个元素为基准
        int key=begin;
        int target=nums[key];
        while(left<right)
        {
            // 必须加等号,否则数组左右两端的数相等时会死循环
            while(left<right&&nums[right]>=target) right--;
            // 必须加等号,否则数组左右两端的数相等时会死循环
            while(left<right&&nums[left]<=target) left++;
            // 满足条件时,交换nums[left]和nums[right]
            if(left<right)
                swap(nums[left],nums[right]);
            

        }
        // 将基准交换到应该到达的位置
        swap(nums[key],nums[left]);
        // 快排左数组
        quicksort(nums,begin,left-1);
        // 快排右数组
        quicksort(nums,left+1,end);
    }
};

还有一种做法是使用堆。可以使用c++自带的堆或者手写一个堆。

c++自带的堆解决这道题比较简单,就不多说了,附上代码就行:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int,vector<int>,less<int>> q;
        for(int num:nums)
        {
            q.push(num);
        }
        while(--k)
            q.pop();
        return q.top();
    }

};

手写堆对我来说是第一次,因此有些许困难,看了看题解,总结了几点重要的(针对于使用数组来构建的堆):

1.堆中节点的索引为n,则其父节点的索引为(n-1)/2

2.堆中节点的索引为n,则其左子节点索引为n*2+1,右子节点索引为n*2+2

3.堆中最后一个非叶子节点的索引为(nums.size()-2)/2

4.堆中最重要的函数是adjust_heap(int index),其主要功能是建立以index为根的大顶堆。比较index和其左右子节点的大小,来选择大顶堆的根,然后递归调用adjust_heap,重新维护以其子节点为根的大顶堆。

相关的链接和参考:

https://leetcode-cn.com/problems/kth-largest-element-in-an-array/solution/by-yi-xie-zhi-qiu-30u-01fe/

https://leetcode-cn.com/problems/kth-largest-element-in-an-array/comments/ 

代码如下:

class Solution {
public:
    // 用数组heap模拟大顶堆
    vector<int> heap;
    int findKthLargest(vector<int>& nums, int k) {
        // 根据nums数组创建堆
        createheap(nums);
        // 弹出前k-1个头节点
        while(--k)
        pop();
        return heap[0];
    }
    // 弹出大顶堆头部
    void pop()
    {
        // 交换头节点和尾节点
        swap(heap[0],heap[heap.size()-1]);
        // 删除尾节点
        heap.erase(heap.end()-1);
        // 调整因为头节点交换而带来的影响
        // 即把头节点交换下去,重新构建大顶堆
        adjust_heap(0);
        return;
    }
    // 根据nums创建大顶堆
    void createheap(vector<int> &nums)
    {
        // 复制nums到heap中
        for(int num:nums)
            heap.push_back(num);
        // 从heap中的最后一个非叶子节点开始,调整heap为大顶堆
        for(int i=nums.size()-2;i>=0;i--)
        {
            adjust_heap(i);
        }

    }
    // 该函数的作用是,以index为根,使得该树为一个大顶堆
    void adjust_heap(int index)
    {
        // 得到index节点的左右节点
        int left=index*2+1;
        int right=index*2+2;
        int max=index;
        int len=heap.size();
        // 比较index和左右节点,得到最大的那个节点
        if(left<len&&heap[left]>heap[max])
            max=left;
        if(right<len&&heap[right]>heap[max])
            max=right;
        if(index!=max)
        {
            // index和最大的节点互换,使得最大的节点来当根节点,
            swap(heap[max],heap[index]);
            // 由于互换节点,以max节点为根的大顶堆被破坏,需要重新维护该大顶堆
            adjust_heap(max);
        }
        return;
    }

};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值