题目链接
题目思路
这个题最容易的思想就是先进行排序,排序完从后往前数k位即可。这个时间复杂度为O(nlogn),但是我们其实没必要对整个数组进行完全的排序。
第二种方法的有化解有优先队列(大顶堆),我们维护大小为k的大顶堆,然后遍历完整个数组之后,我们的大顶堆中存放的是前k个最大的元素,我们一个一个取出来直到最后那个就行。
这样仍然是会涉及到堆的重排序问题,依然是O(nlogn)的时间复杂度,不过这个算法会比较容易想。
第三种方法就是直接使用快速排序的划分思想,因为我们快排每次划分后的中轴都是固定好位置的,而它的左边元素都比它小,右边元素都比它大。
我们可以利用这个思想,直接找到对应的中轴,而不需要去考虑剩余那些元素的相互间的有序性。
代码实现
class Solution {
public int findKthLargest(int[] nums, int k) {
int left = 0;
int right = nums.length - 1;
int length = nums.length;
//因为k一定存在,所以可以写永久循环
while(true){
//获取当前返回的下标
int cur = helper(nums,left,right);
//因为是第K大的元素,其下标就是length-k
if(cur == length - k){
return nums[cur];
}else if( cur > length - k){
right = cur - 1;
}else{
left = cur + 1;
}
}
}
private int helper(int[] nums, int l, int r){
int left = l;
int right = r;
int flag = nums[left++];
//这里一定要写小于等于,不然会把有序的数组变无序呜呜呜
while(left <= right){
//右边大于等于左手边flag位无所谓,重要的是还是得left<=right
while(left <= right && nums[right] >= flag){
right--;
}
//左手边不能出现和flag位相等的数据。
while(left <= right && nums[left] < flag){
left++;
}
//如果两个指针相与,直接跳出循环加快速度。
if(left >= right){
break;
}
int temp = nums[right];
nums[right] = nums[left];
nums[left] = temp;
}
//交换浮标位和右指针位。
nums[l] = nums[right];
nums[right] = flag;
return right;
}
}
总结
这个题三个月前做过一次,今天每日一题又做了一次,虽然还是被边界条件卡了下,但是只WA了一次,比上次要好点。
快排分区的模板代码
private int partition(int[] array, int start, int end) {
int pivot = array[start];// 采用子序列的第一个元素作为枢纽元素
while (start < end) {//
// 从后往前在后半部分中寻找第一个小于枢纽元素的元素
while (start < end && array[end] >= pivot) {
--end;
}
// 将这个比枢纽元素小的元素交换到前半部分
swap(array, start, end);
// 从前往后在前半部分中寻找第一个大于枢纽元素的元素
while (start < end && array[start] <= pivot) {
++start;
}
swap(array, start, end);// 将这个枢纽元素大的元素交换到后半部分
}
return start;// 返回枢纽元素所在的位置
}