在未排序數組中找到第 k 個最大 元素。注意是排序後的第 k 位,而非第 k 個不重複元素。
[3,2,1,5,6,4], k=2 → 返回 5| 解法 | 時間複雜度 | 空間複雜度 | 核心思想 | 適用場景 |
|---|---|---|---|---|
| 全量排序 | O(n log n) | O(1) 或 O(log n) | 排序後取倒數第 k 個 | 代碼最簡、n 較小、面試首選 |
| 最小堆 (大小 k) | O(n log k) | O(k) | 維護 k 個最大元素,堆頂即答案 | n 極大、k 較小、流式數據 |
| 快速選擇 | 平均 O(n) 最壞 O(n²) | O(1) 原地 | Partition 思想,只遞歸目標分區 | 追求極致性能、可接受最壞情況 |
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums); // 升序
return nums[nums.length - k];
}
public int findKthLargest(int[] nums, int k) {
// 小頂堆,堆頂最小
PriorityQueue minHeap = new PriorityQueue<>(k);
for (int num : nums) {
minHeap.offer(num);
if (minHeap.size() > k) {
minHeap.poll(); // 移除最小,保持堆大小 = k
}
}
return minHeap.peek(); // 堆頂即第 k 大
}
public int findKthLargest(int[] nums, int k) {
int target = nums.length - k; // 第 k 大 = 升序索引 target
int left = 0, right = nums.length - 1;
while (true) {
int pivotIndex = partition(nums, left, right);
if (pivotIndex == target) return nums[pivotIndex];
else if (pivotIndex < target) left = pivotIndex + 1;
else right = pivotIndex - 1;
}
}
private int partition(int[] nums, int left, int right) {
// 隨機選擇 pivot 避免最壞 O(n²)
int random = left + new Random().nextInt(right - left + 1);
swap(nums, random, right);
int pivot = nums[right];
int storeIndex = left;
for (int i = left; i < right; i++) {
if (nums[i] < pivot) {
swap(nums, storeIndex++, i);
}
}
swap(nums, storeIndex, right);
return storeIndex;
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;
}
n - kPriorityQueue 默認最小堆,無需自定義比較器Arrays.sort(nums); return nums[k-1]; 或 最大堆