LC 215:数组中的第 K 个最大元素
本题思路根据 wisdompeak 大神的 题解 整理。
题目
给定整数数组 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 <= 10^5
-10^4 <= nums[i] <= 10^4
方法 1:堆排序
思路
维护一个容量为 $k$ 的小顶堆。遍历数组,考虑以下两种情况:
- 小顶堆的元素个数小于 $k$,直接加入当前元素。
- 小顶堆的元素个数等于 $k$,如果当前元素大于堆顶元素,则移除堆顶元素并加入当前元素。
复杂度分析
- 时间复杂度: $\mathcal{O}(n\log k)$。
- 空间复杂度: $\mathcal{O}(k)$。
代码:直接使用优先队列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Solution215 {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<>(k);
for (int num : nums) {
if (pq.size() < k) {
pq.offer(num);
} else if (pq.peek() < num) {
pq.poll();
pq.offer(num);
}
}
return pq.peek();
}
}
代码:手写堆排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Solution215 {
public int findKthLargest(int[] nums, int k) {
buildMinHeap(nums, k);
for (int i = k; i < nums.length; i++) {
if (nums[i] < nums[0]) {
continue;
}
swap(nums, 0, i);
minHeapify(nums, 0, k);
}
return nums[0];
}
private void minHeapify(int[] nums, int root, int size) {
int left = root * 2 + 1, right = left + 1, min = root;
if (left < size && nums[left] < nums[min]) {
min = left;
}
if (right < size && nums[right] < nums[min]) {
min = right;
}
if (min != root) {
swap(nums, root, min);
minHeapify(nums, min, size);
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
private void buildMinHeap(int[] nums, int k) {
for (int i = k / 2 - 1; i >= 0; i--) {
minHeapify(nums, i, k);
}
}
}
方法 2:二分搜索
思路
使用二分搜索模板。
复杂度分析
- 时间复杂度: $\mathcal{O}(n\log C)$。在本题的数据范围下,$C$ 约为 15。
- 空间复杂度: $\mathcal{O}(1)$。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Solution215 {
public int findKthLargest(int[] nums, int k) {
int left = -10001, right = 10001;
while (left + 1 < right) {
int mid = (left + right) >> 1;
if (count(nums, mid) >= k) {
left = mid;
} else {
right = mid;
}
}
return left;
}
private int count(int[] nums, int target) {
int res = 0;
for (int num : nums) {
res += num >= target ? 1 : 0;
}
return res;
}
}
方法 3:快速选择
思路
使用快速选择模板。
复杂度分析
- 时间复杂度: $\mathcal{O}(n)$。
- 空间复杂度: $\mathcal{O}(\log n)$。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Solution215 {
public int findKthLargest(int[] nums, int k) {
return quickSelect(nums, k, 0, nums.length - 1);
}
private int quickSelect(int[] nums, int k, int left, int right) {
int pivot = nums[(left + right) / 2];
int i = left, j = right;
int t = left;
while (t <= j) {
if (nums[t] < pivot) {
swap(nums, i, t);
i++;
t++;
} else if (nums[t] > pivot) {
swap(nums, j, t);
j--;
} else {
t++;
}
}
if (right - j >= k) {
return quickSelect(nums, k, j + 1, right);
} else if (right - i + 1 >= k) {
return pivot;
} else {
return quickSelect(nums, k - (right - i + 1), left, i - 1);
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
This post is licensed under CC BY 4.0 by the author.