题目描述
题目分析:
这题是出现过很多次的经典题目,但是这次是正好在算法课上学习了selection算法后,找来实践的。上次这道题目选择的是使用优先队列,用造好的轮子毕竟是简单,这次的selection算法是选择的Wikipedia的Quickselect词条下的伪代码实现。其中有两种,一种是尾递归实现,另一种是用循环模拟递归实现,不需要调用函数的循环实现当然更快,因此选用了循环的方式实现。以下给出Wikipedia此词条的超链接,已经参考的伪代码https://en.wikipedia.org/wiki/Quickselect
时间复杂度分析:
Quickselect的算法的时间复杂度在好的情况下,可以达到O(nlogn),但是这依赖于能否选择到好的pivot,当最坏情况发生,时间复杂度会退化到O(
n2
)。我尝试了两次,一次是使用固定的pivot: pivotIndex = (l + r) >>1
,另一次选择使用了随机数 pivotIndex = int(rand() % (r-l +1)) + l
实际上,使用随机数的情况大部分消耗时间都是和固定的一样,但是有时候能快30%,下图是其中最快的一次。
实际上,我曾经跑过discuss中其他排名较高的人的代码,都需要50ms左右,没有能进10ms的,而这个算法可以稳定在9ms以内,可以说是非常快了。所以实际时间复杂度可以说是非常低的
代码
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
// change k from kth largest to kth smallest
srand((unsigned)time(NULL));
k = nums.size() - k;
int l = 0, r = nums.size()-1;
while (true) {
if (l == r) return nums[l];
int pivotIndex = int(rand() % (r-l +1)) + l;
pivotIndex = partition(nums, l, r, pivotIndex);
if (k == pivotIndex) return nums[k];
else if (k < pivotIndex) r = pivotIndex -1;
else l = pivotIndex +1;
}
}
int partition(vector<int>& nums, int l, int r, int pivotIndex) {
int pivotValue = nums[pivotIndex], storeIndex = l;
swap(nums[pivotIndex], nums[r]);
for (int i = l; i < r; i++) {
if (nums[i] < pivotValue) {
swap(nums[storeIndex], nums[i]);
storeIndex++;
}
}
swap(nums[r], nums[storeIndex]);
return storeIndex;
}
};