题目描述:
难度:中等 相关标签:数组、分治、快速选择、排序、堆(优先队列) 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: [3,2,1,5,6,4], k = 2 输出: 5 示例2: 输入: [3,2,3,1,2,4,5,5,6], k = 4 输出: 4 提示: 1 <= k <= nums.length <= 105 -104<= nums[i] <= 104
解决:
思路:基于快速排序的选择方法(快速选择)
我们知道,每次经过「划分」操作后,我们一定可以确定一个目标整数(每次默认最左边的)的最终位置 index,那么我们就在每次确定那个index后,就判断是否是符合要求的第K个最大整数的下标 p( = nums.lpength - k),
index = p ,直接返回那个目标整数就行
如果不是的话,
当index > p 时,则向 [ l, index - 1 ] 递归继续寻找
而当index < p 时,则向 [ index + 1, r ] 递归继续寻找
class Solution {
public int findKthLargest(int[] nums, int k) {
int num = quickSelect( nums, 0, nums.length - 1, k );
return num;
}
// 快速选择
public int quickSelect( int[] nums, int l, int r, int k ) {
// 获取这一趟快排后确认位置的 目标整数 的下标
int index = quickSort( nums, l, r );
// 如果这个确认的整数的下标刚好 == k,返回即可
// 如果这个确认的整数的下标 > nums.length - k,则向 [ l, index - 1 ]递归继续寻找
// 如果这个确认的整数的下标 < nums.length - k,则向 [ index + 1, r ]递归继续寻找
if ( index == nums.length - k ) {
return nums[ index ];
} else {
return index > nums.length - k ? quickSelect( nums, l, index - 1, k ) : quickSelect( nums, index + 1, r, k );
}
}
// 快排的划分
public int quickSort( int[] nums, int l, int r ) {
int n = nums[ l ]; // 默认每一趟快排的目标整数位最左边的整数
int index = l; // 记录目标整数的下标
while ( l < r ) {
while( l < r ) {
if ( nums[ r ] < n ) {
swap(nums, l, r);
index = r;
break;
}
r --;
}
while ( l < r ) {
if ( nums[ l ] > n ) {
swap(nums, l, r);
index = l;
break;
}
l++;
}
}
return index;
}
// 定义交换方法
public void swap( int[] nums, int i, int j ) {
int temp = nums[ i ];
nums[ i ] = nums[ j ];
nums[ j ] = temp;
}
}
复杂度分析:时间复杂度:O(n),如上文所述,证明过程可以参考「《算法导论》9.2:期望为线性的选择算法」。
空间复杂度:O(\log n),递归使用栈空间的空间代价的期望为 O(logn)。