题目描述:
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.
For example, Given [3,2,1,5,6,4] and k = 2, return 5.
解法一:
使用C++中提供的函数nth_element,直接找出所求元素,时间复杂度为O(n)。最后用时很短,仅需9ms。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
nth_element(nums.begin(), nums.end() - k, nums.end());
return nums[nums.size() - k];
}
};
解法二(使用分治算法):
根据书上的随机分治算法,首先从数组中随机选取一个元素v,重新安排数组,把比v大的元素放到数组左边,比v小的数放在数组右边,v的存储位置m就说明v是数组中第m+1大的元素。如果m+1=k,说明v刚好是我们要找的元素;如果m+1< k,说明要找的元素在数组的右半段;反之,要找的元素在数组的左半段。算法如下图所示,图中
SL
表示重排后数组的左半边,
Sv
表示数组中等于v的部分,
SR
表示数组的右半边。
算法中还有一点需要考虑:选择v后,如何将数组重排?实际上,可以先把目标元素存到数组末尾,然后遍历数组,同时维护一个storeidx(初值为0),如果遇到比目标元素大的元素,则与storeidx所代表的元素进行交换,然后将storeidx的值增加1,遍历完成后storeidx的值就应该是目标元素最后需要存放的数组下标。这样的话这一部分的空间复杂度仅为O(1)。
整体代码如下:
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
return selection(nums,0,nums.size() - 1,k);
}
int selection(vector<int>& nums, int left, int right, int k){
int random, storeidx, nums_rand;
int length = right - left + 1;
// pick a random element in array
srand(time(NULL));
random = rand() % length + left;
nums_rand = nums[random];
// put the elements larger than nums_rand to left, others to right
swap(nums[random],nums[right]);
storeidx = left;
for(int i = left;i < right;i++){
if(nums[i] > nums_rand){
swap(nums[storeidx], nums[i]);
storeidx++;
}
}
swap(nums[storeidx], nums[right]);
// find the kth largest number
if((storeidx - left + 1) == k) return nums_rand;
else if((storeidx - left + 1) > k) return selection(nums,left,storeidx - 1,k);
else return selection(nums, storeidx + 1, right, k - (storeidx - left + 1));
}
void swap(int &a,int &b){
int temp = a;
a = b;
b = temp;
}
};
该算法的思路其实和快排一致,运行速度也很快,最后运行时间为9ms。