Top K算法是一种快速查找数组中前K大元素的算法。常见的 Top K 算法有:
- 基于快速排序的方法。使用快速排序将数组分割为K大元素所在的数组。时间复杂度O(nlogK)。
- 基于堆的方法。维护一个大小为K的最小堆,遍历数组将元素插入堆中,并在插入后调整堆。时间复杂度O(nlogK)。
- 基于快速选择的方法。使用快速选择算法找到数组第K大元素。时间复杂度O(n)。
我们这里实现第3种基于快速选择的Top K算法:
public int[] topK(int[] nums, int k) {
int[] result = new int[k];
quickselect(nums, 0, nums.length - 1, k, result);
return result;
}
private void quickselect(int[] nums, int left, int right, int k, int[] result) {
if (left == right) return;
int pivotIndex = partition(nums, left, right);
if (pivotIndex > k - 1) {
quickselect(nums, left, pivotIndex - 1, k, result);
} else if (pivotIndex < k - 1) {
for (int i = left; i <= pivotIndex; i++) {
result[i - left] = nums[i];
}
quickselect(nums, pivotIndex + 1, right, k - (pivotIndex - left + 1), result);
} else {
for (int i = left; i <= right; i++) {
result[i - left] = nums[i];
}
}
}
private int partition(int[] nums, int left, int right) {
// 选择最后一个元素为pivot
int pivot = nums[right];
int j = left;
for (int i = left; i < right; i++) {
if (nums[i] <= pivot) {
swap(nums, i, j);
j++;
}
}
swap(nums, j, right);
return j;
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
算法流程:
- 递归终止:当left == right,返回。
- 选择pivot并划分数组,返回pivot位置index。
- 如果pivotIndex > k – 1,递归查找左部分top k。
- 如果pivotIndex < k – 1, result数组前pivotIndex – left + 1个元素为top k,递归查找右部分top k – (pivotIndex – left + 1)。
- 否则找到top k,返回。
- 不断递归划分与查找,直到找到top k。
时间复杂度O(n),空间复杂度O(k)。
所以,Top K算法的关键是:
- 基于快速选择算法,使用pivot划分数组。
- 递归终止条件:left == right。
- 根据pivotIndex判断top k在左部分、右部分或找到,相应递归或返回。
- 通过不断递归划分与查找,找到top k。
与快速选择算法相比,找到top k后直接返回result,不继续递归。