1. 题号和题目名称
- 数组中的第K个最大元素
2. 题目叙述
给定整数数组 nums
和整数 k
,请返回数组中第 k
个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
你必须设计并实现时间复杂度为
O
(
n
)
O(n)
O(n) 的算法解决此问题。
3. 模式识别
本题的核心是在无序数组中找出第 k
大的元素。可以联想到排序相关的算法,不过要满足
O
(
n
)
O(n)
O(n) 时间复杂度,需要使用一些特殊的算法,如快速选择算法或堆排序。
4. 考点分析
- 排序算法的理解:对常见排序算法(如快速排序、堆排序)的原理和复杂度有深入理解。
- 分治思想:快速选择算法基于分治思想,需要掌握分治算法的设计和实现。
- 数据结构的运用:堆排序需要使用堆这种数据结构,要理解堆的性质和操作。
5. 所有解法
解法一:排序法
将数组进行排序(如使用快速排序、归并排序等),然后直接返回第 k
大的元素。这种方法的时间复杂度为
O
(
n
l
o
g
n
)
O(n log n)
O(nlogn),不满足题目要求的
O
(
n
)
O(n)
O(n) 复杂度。
解法二:堆排序
使用堆来维护前 k
大的元素。可以使用最小堆,遍历数组,将元素依次加入堆中,当堆的大小超过 k
时,移除堆顶元素(最小元素)。最后堆顶元素即为第 k
大的元素。时间复杂度为
O
(
n
l
o
g
k
)
O(n log k)
O(nlogk)。
解法三:快速选择算法
这是本题的最优解法。快速选择算法基于快速排序的思想,通过选择一个基准元素,将数组分为两部分,根据基准元素的位置和 k
的大小关系,决定在左半部分或右半部分继续查找,平均时间复杂度为
O
(
n
)
O(n)
O(n)。
6. 最优解法(快速选择算法)的 C 语言代码
#include <stdio.h>
// 交换两个元素的函数
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
// 快速选择函数,用于查找第 k 小的元素
int quickselect(int *nums, int l, int r, int k) {
if (l == r)
return nums[l]; // 修正此处,返回 nums[l]
int partition = nums[l];
int i = l - 1, j = r + 1;
while (i < j) {
do i++; while (nums[i] < partition);
do j--; while (nums[j] > partition);
if (i < j) {
swap(&nums[i], &nums[j]);
}
}
if (k <= j)
return quickselect(nums, l, j, k);
else
return quickselect(nums, j + 1, r, k);
}
// 查找第 k 大的元素
int findKthLargest(int *nums, int numsSize, int k) {
return quickselect(nums, 0, numsSize - 1, numsSize - k);
}
7. 复杂度分析
- 时间复杂度:平均情况下为 O ( n ) O(n) O(n),最坏情况下为 O ( n 2 ) O(n^2) O(n2)。在每次分区操作中,能够将问题规模大致缩小一半,因此平均时间复杂度为线性。
- 空间复杂度:平均情况下为 O ( l o g n ) O(log n) O(logn),最坏情况下为 O ( n ) O(n) O(n)。主要的空间开销在于递归调用栈的深度。