力扣 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
分析
偷懒方法很多,可以直接sort后输出n-k位置的元素,也可以用priority_queue声明一个大顶堆弹出k-1个元素,堆顶元素即为答案。
不过这个题目主要是考察快速排序,官方题解给的比较乱,给出一个我认为看的比较懂的
代码
class Solution {
public:
int QuickSort(vector<int> &nums, int begin, int end, int target) {
if (begin >= end)
return nums[end];
int i = begin;
int j = end;
int index = rand() % (end - begin);
swap(nums[begin], nums[begin + index]);
int tmp = nums[begin];
while (i != j) {
while (j > i && nums[j] >= tmp) {
--j;
}
while (j > i && nums[i] <= tmp) {
++i;
}
if (i != j)
swap(nums[i], nums[j]);
}
swap(nums[begin], nums[i]);
if (i == target)
return nums[i];
if (target < i)
return QuickSort(nums, begin, i - 1, target);
else
return QuickSort(nums, i + 1, end, target);
}
int findKthLargest(vector<int> &nums, int k) {
return QuickSort(nums, 0, nums.size() - 1, nums.size() - k);
}
};
时间复杂度为O(log n),因为每次只需要遍历一半的空间,另外一半不需要进行排序
代码解释及分析
代码不难懂,基本上懂了快速排序的原理都可以看懂,主要解释几个可能会产生疑惑的地方:
int index = rand() % (end - begin);
swap(nums[begin], nums[begin + index]);
因为快速排序只有随机选择基准节点时复杂度才为O(nlog n),所以这个地方需要生成随机数来负责基准节点的选取,为了代码的方便编写,随机选取了基准节点后可以直接和begin位置的节点进行交换即可,在912. 排序数组这个题目中也可以得到验证,直接选取begin位置的节点作为哨兵节点时是会进行超时的(或者运行时间会长很多),只有随机选择节点才能 通过。
为什么外层的while循环体内部还需要判断一次i != j并进行交换
while (j > i && nums[j] >= tmp) {
--j;
}
while (j > i && nums[i] <= tmp) {
++i;
}
if (i != j)
swap(nums[i], nums[j]);
观察这段代码的两个while条件的判断,什么情况下会出现 i != j,只有当num[j] < tmp和nums[i] > tmp的时候,这个时候两个哨兵节点是还没有相交的,还需要继续走,这个时候就可以将num[i]和num[j]进行交换,那样位置i的节点就会小于tmp,同理位置j的节点会大于tmp,这个时候两个哨兵节点就可以继续往中间走了