LeetCode 215. 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.
输入示例:
Example 1:
Input: [3,2,1,5,6,4] and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4
Note:
You may assume k is always valid, 1 ≤ k ≤ array’s length.
题解:
对这个问题来讲,很容易想到先排序,但是这样做的时间复杂度是O(nlogn),因为我们只需要找到第K大的数即可(注意这里指的是排序后数组第K大的数而不是去重后的数组)。我们可以采用与快排一样的思路,先找一个pivot,然后将比它大的元素放它右边,小的放左边,然后看看pivot在数组中的index:
- index 等于 array‘s length – k,则返回它
- index 小于 array‘s length – k,则对pivot的右边进行相同操作
- index 大于 array‘s length – k,则对pivot的左边进行相同操作
Code:
递归版本:
void swap(vector<int>&nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
int helper(vector<int>& nums, int lo, int hi, int k) {
if (lo == hi) return nums[lo];
int pivot = nums[hi], l = lo, r = hi;
while (l < r) {
while (nums[l] <= pivot && l < r) ++l;
while (nums[r] >= pivot && l < r) --r;
swap(nums, l, r);
}
swap(nums, l, hi);
if (l == nums.size() - k) return nums[l];
else if (l < nums.size() - k) return helper(nums, l + 1, hi, k);
else return helper(nums, lo, l - 1, k);
}
int findKthLargest(vector<int>& nums, int k) {
return helper(nums, 0, nums.size() - 1, k);
}
非递归版本:
void swap(vector<int>&nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
int helper(vector<int>& nums, int lo, int hi) {
if (lo == hi) return lo;
int pivot = nums[hi], l = lo, r = hi;
while (l < r) {
while (nums[l] <= pivot && l < r) ++l;
while (nums[r] >= pivot && l < r) --r;
swap(nums, l, r);
}
swap(nums, l, hi);
return l;
}
int findKthLargest(vector<int>& nums, int k) {
int lo = 0, hi = nums.size() - 1;
while (true) {
int pivot = helper(nums, lo, hi);
if (pivot == nums.size() - k) return nums[pivot];
if (pivot < nums.size() - k) lo = pivot + 1;
else hi = pivot - 1;
}
}
复杂度分析:
首先我们假设pivot找得很好,每次刚刚好都落在数组的中点,每次扫描一次截出来的数组,这样的话:
1. 数组大小为n,需n次比较
2. 数组大小为n/2,需n/2次比较
3. 数组大小为n/4,需n/4次比较
4. …
5. 数组大小为1,即找到目标元素
可以看到从第0趟到第logn趟,其比较次数是一个递减的等比数列,所以总复杂度我们可以用首项来表示,即O(n)。这里数组大小减小为1才能得出结果并不是必然的,即有机会在中间层就已经找到一个pivot刚好就是目标元素。
我们都知道quicksort最差会退化成O(n^2),当给定数组是有序时,这个算法最差也会退化至O(n^2),但对大部分情况来讲,只需要O(n),相比先排序,它不需要每趟都遍历整个完整的数组,也不需要进行合并的工作。同时,非递归版本比递归版本有更好的空间复杂度。
其它解法:
- 将数组元素压入
priority_queue
中,其底层数据结构实现是大根堆,最后弹出k个元素即为第k大的元素,复杂度为O(nlogn),其复杂度主要在插入和弹出时对堆的结构的维护。 - …