给定整数数组 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 <= 1 0 5 10^5 105
- 1 0 4 10^4 104 <= nums[i] <= 1 0 4 10^4 104
堆排序,使用优先队列 PriorityQueue
时间复杂度: O ( N l o g K ) O(NlogK) O(NlogK)
// https://leetcode.cn/problems/kth-largest-element-in-an-array/
import org.junit.jupiter.api.Test;
import java.util.PriorityQueue;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class Solution {
//思想:堆排序,
public int findKthLargest(int[] nums, int k) {
//优先队列,默认情况下,优先级队列的对象按自然顺序排序
//PriorityQueue调用remove()或poll()方法,返回的总是优先级最高的元素。
PriorityQueue<Integer> pq = new PriorityQueue<>();//优先队列保存数值大小的前k个数
for (int num : nums) {
pq.add(num);
//队列长度大于输入的 k
if (pq.size() > k) {
pq.poll();
}
}
//System.out.println(pq);
return pq.peek(); //peek():检索但不删除此队列的头部,如果此队列为空,则返回null。
}
//暴力
public int findKthLargest2(int[] nums, int k) {
Arrays.sort(nums);
return nums[nums.length - k];
}
@Test
public void test() {
Solution s = new Solution();
assertEquals(5, s.findKthLargest(new int[]{3, 2, 1, 5, 6, 4}, 2));
assertEquals(4, s.findKthLargest(new int[]{3, 2, 3, 1, 2, 4, 5, 5, 6}, 4));
assertEquals(3, s.findKthLargest(new int[]{3, 2, 3, 3, 1, 2, 4, 5, 5, 6}, 5));
}
}
快排:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
// Time Complexity: O(N)
// Space Complexity: O(logN)
class Solution {
public int findKthLargest(int[] nums, int k) {
return quickSort(nums, 0, nums.length - 1, k);//起始位置,终止位置
}
public int quickSort(int[] nums, int left, int right, int k) {
// 第 1 大的数,下标是 len - 1;
// 第 2 大的数,下标是 len - 2;
// ...
// 第 k 大的数,下标是 len - k;
//随机取一个数作为index
int index = randomParition(nums, left, right);
//刚好是第k大
if (index == k - 1) {
return nums[index];
}
else {
//取的第k大元素在index左边
if (index > k - 1){
return quickSort(nums, left, index - 1, k);
}
//取的第k大元素在index右边
else{
return quickSort(nums, index + 1, right, k);
}
}
}
public int randomParition(int[] nums, int left, int right) {
//在[l,r]中随机取一个数
int i = (int) (Math.random() * (right - left)) + left;
//交换的目的是:将随机取出来的值,先移动到数组的尾端,然后将该值前面的数分区,此时的nums[i]为快排的pivot
swap(nums, i, right);
//进行分区
return partition(nums, left, right);
}
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
//分区函数,比元素小的放右边,大的放左边
public int partition(int[] nums, int left, int right) {
//随机取出的数
int pivot = nums[right];
//记录最右边位置
int rightmost = right;
while (left <= right) {
while (left <= right && nums[left] > pivot) {
left++;
}
while (left <= right && nums[right] <= pivot) {
right--;
}
if (left <= right) {
swap(nums, left, right);
}
}
swap(nums, left, rightmost);
return left;//分界线的值
}
@Test
public void test() {
Solution s = new Solution();
assertEquals(5, s.findKthLargest(new int[]{3, 2, 1, 5, 6, 4}, 2));
assertEquals(4, s.findKthLargest(new int[]{3, 2, 3, 1, 2, 4, 5, 5, 6}, 4));
assertEquals(3, s.findKthLargest(new int[]{3, 2, 3, 3, 1, 2, 4, 5, 5, 6}, 5));
}
}