题目
有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数。
给定一个整数数组 a ,同时给定它的大小n和要找的 k ,请返回第 k 大的数(包括重复的元素,不用去重),保证答案存在。
要求:时间复杂度 O(nlogn),空间复杂度 O(1)。
数据范围:0≤n≤1000, 1≤K≤n,数组中每个元素满足 0≤val≤10000000。
示例1
输入:[1,3,5,2,2],5,3
返回值:2
示例2
输入:[10,10,9,9,8,7,5,6,4,3,4,2],12,3
返回值:9
说明:去重后的第3大是8,但本题要求包含重复的元素,不用去重,所以输出9。
思路
快排分区partition思想,当分区函数返回的索引值 = n - k,该分区元素就是要找的元素。时间复杂度O(n)。
代码
import java.util.*;
public class Solution {
public int findKth(int[] a, int n, int K) {
//第k大元素的索引为n - k
//[1,2,3,4,5]第2大元素4对应的索引为5 - 2 = 3
return findKthLargestInternal(a, 0, a.length - 1, a.length - K);
}
/**
* 在nums[l...r]找到索引为k元素
* @param nums
* @param l
* @param r
* @param k
* @return
*/
private int findKthLargestInternal(int[] nums, int l, int r, int k) {
if (l > r) {
//空区间
return -1;
}
int p = partition(nums, l, r);
if (p == k) {
//此时索引p对应的元素恰好就是要查找的元素
return nums[p];
} else if (k > p) {
//在右半区间接着找
return findKthLargestInternal(nums, p + 1, r, k);
}
//此时k < p,在左半区间找
return findKthLargestInternal(nums, l, p - 1, k);
}
/**
* 分区函数
* @param nums
* @param l
* @param r
* @return
*/
private int partition(int[] nums, int l, int r) {
//默认选择第一个元素作为分区点
int v = nums[l];
//i是当前处理的元素
//arr[l + 1...j] < v
//最开始没有元素 < v
int j = l;
//arr[j + 1...i) >= v也是空区间
for (int i = l + 1; i <= r; i++) {
if (nums[i] < v) {
swap(nums, j + 1, i);
j++;
}
}
//j对应 <v 的最后一个元素
swap(nums, l, j);
return j;
}
/**
* 交换三连操作
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}