Leetcode Algorithm 215. Kth Largest Element in an Array
Kth Largest Element in an Array
给定一个乱序的非空整型数数组,返回数组中第k大的数
解题思路
解法1
先对数组
a
利用排序算法从大到小排好序,最后直接取下标
解法2
在快排的过程中,其实我们是不是以及久可以定位到第 k 大的元素了呢?答案是肯定的。下面结合题目给出的样例说明。
有一个数组
回顾一下快排的划分过程(时间复杂度为
O(n)
):
1. 找主元(pivot),一般取开头元素,即
pivot=a[0]=3
;
2. 若对数组从大到小排序,就要从头遍历数组,把大于pivot的数字与小于pivot的数字分成两块;
3. 把pivot换回去两块中间。
假设划分完成之后,pivot在下标为
p
的位置,它之前的
若 p+1==k 或 p==k−1 ,正好是我们想要的答案。但是事情不一定会按照这个剧情展开,有可能 p<k−1 ,也有可能 p>k−1 。
假如
p<k−1
,说明第
k
大的数在比pivot小的那堆数里面,即
这样,我们将原本的问题就按照两种不同的情况各自分成了一个子问题,然后在指定的数组下标范围内继续用快排划分的过程找出答案。对于平均时间复杂度,有递归方程:
用主定理可求得平均时间复杂度为 O(n) 。
代码与测试样例
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
int start = 0;
int end = nums.size();
int p = partition(nums, start, end);
while (p != k - 1) {
if (p > k - 1)
end = p;
else
start = p + 1;
p = partition(nums, start, end);
}
return nums[p];
}
int partition(vector<int>& nums, int start, int end) {
int pivot = nums[start];
int last_small = start;
for (int i = start + 1; i < end; i++) {
if (nums[i] > pivot) {
last_small++;
if (i != last_small) {
int temp = nums[i];
nums[i] = nums[last_small];
nums[last_small] = temp;
}
}
}
nums[start] = nums[last_small];
nums[last_small] = pivot;
return last_small;
}
};
int main() {
int nums[] = { 3, 2, 1, 5, 6, 4 };
int n = 6;
vector<int> v_num(nums, nums + n);
Solution s;
for (int i = 0; i < v_num.size(); i++) {
cout << s.findKthLargest(v_num, i+1) << endl;
}
return 0;
}
输出
6
5
4
3
2
1
知识点
快排
平均时间复杂度为 O(nlogn) ,最坏情况是 O(n2) ,原因是划分出现不平衡,每次找到的pivot恰好是数组的最小元素或最大元素,导致子问题规模只减少到 T(n−1) 。
void quick_sort(vector<int>& nums) {
int start = 0;
int end = nums.size();
int p = partition(nums, start, end);
partition(nums, start, p);
partition(nums, p, end);
}
int partition(vector<int>& nums, int start, int end) {
int pivot = nums[start];
int last_small = start;
for (int i = start + 1; i < end; i++) {
if (nums[i] > pivot) { // 如果是从小到大排就改成 '<'
last_small++;
if (i != last_small) {
int temp = nums[i];
nums[i] = nums[last_small];
nums[last_small] = temp;
}
}
}
nums[start] = nums[last_small];
nums[last_small] = pivot;
return last_small;
}
找乱序数组的中位数
若
n
为奇数,中位数实则为求第
double findMedian(vector<int>& nums) {
int start = 0;
int end = nums.size();
int k1, k2;
int k2_flag = false;
k1 = end / 2 + 1;
if (end % 2 == 0)
k2 = k1 - 1;
else
k2 = -1;
int p = partition(nums, start, end);
while (p != k1 - 1) {
if (k2 == -1 && p == k2 - 1) {
k2 = p;
k2_flag = true;
cout << "K2 FOUND!" << endl;
}
if (p > k1 - 1)
end = p;
else
start = p + 1;
p = partition(nums, start, end);
}
if (end % 2 != 0)
return nums[k1 - 1];
else {
if (!k2_flag) {
k2 = k1 - 1;
start = 0;
end = p;
p = partition(nums, start, end);
while (p != k2 - 1) {
if (p > k2 - 1)
end = p;
else
start = p + 1;
p = partition(nums, start, end);
}
}
return (nums[k1 - 1] + nums[k2 - 1]) / 2.0f;
}
}