[230529 lc215] 数组中的第K个最大元素

[230529 lc215] 数组中的第K个最大元素

又是一道伤心的题。是上个月旷视一面,面试官让我用快排写这道题,当时脑子很懵,不知道怎么写快排。遗憾离场。

一 题目

215. 数组中的第K个最大元素

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

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

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 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 <= 105
  • -104 <= nums[i] <= 104

二 快速排序原始版

思路:利用快速排序获得升序数组,然后返回 nums[size - k]

快速排序的实现:传入一个乱序数组,选中最右边的元素为主元,在剩余元素中使用双指针的做法,i 指针扫过的值均为小于主元的值,当 j 指针遍历完剩余元素后,交换 i 位置和主元位置上的元素,此时 pivot 就位,然后递归地调用 quickSort 函数对左右两边的子数组进行快排。

代码实现:

class Solution {
private:
    void quickSort(vector<int>& nums, int left, int right) {
        if(right <= left) {
            return;
        }
        int pivot = right;
        int i = left;
        for(int j = left; j < right; ++j) {
            if(nums[j] < nums[pivot]) {
                swap(nums[i], nums[j]);
                ++i;
            }
        }
        swap(nums[i], nums[pivot]);
        quickSort(nums, left, i - 1);
        quickSort(nums, i + 1, right);
    }
public:
    int findKthLargest(vector<int>& nums, int k) {
        int size = nums.size();
        quickSort(nums, 0, size - 1);
        return nums[size - k];
    }
};

三 快速排序优化版

思路:在上面的思路中,是递归地调用 quickSort 使整个数组有序。实际上我们只需要在每趟 quickSort 之后比较主元的下标与 size - k 的大小,然后更新 left 或者 right,对主元左边或者右边的子数组进行 quickSort 就可以了。

快排的实现:quickSort 函数返回快排后主元的下标

代码实现:

class Solution {
private:
    int quickSort(vector<int>& nums, int left, int right) {
        if(right <= left) {
            return left;
        }
        int pivot = right;
        int i = left;
        for(int j = left; j < right; ++j) {
            if(nums[j] < nums[pivot]) {
                swap(nums[i], nums[j]);
                ++i;
            }
        }
        swap(nums[i], nums[pivot]);
        return i;
    }
public:
    int findKthLargest(vector<int>& nums, int k) {
        int size = nums.size();
        int left = 0, right = size - 1;
        while(true) {
            int ret = quickSort(nums, left, right);
            if(ret == size - k) {
                return nums[ret];
            } else if(ret < size - k) {
                left = ret + 1;
            } else {
                right = ret - 1;
            }
        }
        return -1;
    }
};

四 C++ 的优先级队列实现堆排序

思路:把所有元素压入大顶堆中,再逐个弹出元素,直至弹出第 k 个元素。或者维护一个 size 始终为 k 的小顶堆,最后返回堆顶元素。以下为第二种实现。

代码实现:

class Solution {
private:
    class Less{
    public:
        bool operator()(int a, int b) {
			return a > b;
        }
    };
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, Less> que;
        int size = nums.size();
        for(int i = 0; i < size; ++i) {
			que.push(nums[i]);
            if(i >= k) {
                que.pop();
            }
        }
        return que.top();
    }
};

五 手撕堆排序

思路:通过大顶堆堆排序逐元素从大到小构造升序序列,当构造到第 k 大个元素时返回其值。

堆排序的思路:先把原数组初始化为一个大顶堆,此时数组中第一个元素即最大元素,然后交换最大元素和最后一个元素的位置,接着调整前 size - 1 个元素为大顶堆,找到第二大的元素,交换位置。如此迭代,直到找到第 k 大个元素。

代码实现:

class Solution {
private:
    void heapify(vector<int>& nums, int n, int i) {
        int largest = i;
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        if(left < n && nums[left] > nums[largest]) {
            largest = left;
        }
        if(right < n && nums[right] > nums[largest]) {
			largest = right;
        }
        if(largest != i) {
			swap(nums[i], nums[largest]);
            heapify(nums, n, largest);
        }
    }
public:
    int findKthLargest(vector<int>& nums, int k) {
        int size = nums.size();
        for(int i = size / 2 - 1; i >= 0; --i) {
			heapify(nums, size, i);
        }
        for(int i = size - 1; i >= size - k; --i) {
			swap(nums[0], nums[i]);	//此时nums[i]就序
            heapify(nums, i, 0);
        }
        return nums[size - k];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值