1、題目描述
在未排序的數組中找到第 k 個最大的元素。請注意,你需要找的是數組排序後的第 k 個最大的元素,而不是第 k 個不同的元素。
說明:你可以假設 k 總是有效的,且 1 ≤ k ≤ 數組的長度。
2、示例
輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4
輸出: 4
3、題解
解法一:
基本思想:構造前k個最大元素小頂堆,取堆頂,堆頂元素最小
解法二:
基本思想:手動實現堆排序,構造大頂堆,因爲大頂堆最後得到的是從小到大的順序,構造大頂堆後,從堆尾先得到最大元素依次得到第k大元素即可。堆排序(Heap Sort)的時間複雜度是O(nlogn),最壞情況下也是如此。而快速排序(Quick Sort),若初始記錄序列有序,快速排序將退化爲起泡排序(Bubble Sort), 時間複雜度是O(n^2)。
這是堆排序比快速排序的優點
- 堆排序第一步建立大頂堆,從下面節點往上面節點建立,就是保證當前節點i值大於兩個孩子節點2*i+1和2*i+1值
- 對於堆節點nums[start]進行調整,若小於兩個孩子節點nums[i]nums[i+1]中最大的則調整,否則break
- nums[start]大於兩個孩子節點中最小的,調整
- 看調整後start的孩子節點i是否大於i的孩子節點,因此更新start和i
- 堆排序第二步依次交換堆頂和堆底, 這樣堆底就是排序後的最大值,該節點也去除在堆中,大頂堆被破壞重新建立堆,同時堆的範圍改變,節點在逐漸減少
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
//基本思想:排序,取第 k 個
sort(nums.begin(), nums.end());
return nums[nums.size() - k];
}
};
class Solution1 {
public:
int findKthLargest(vector<int>& nums, int k) {
//基本思想:構造前k個最大元素小頂堆,取堆頂,堆頂元素最小
priority_queue<int, vector<int>, greater<int>> q;
for (auto num : nums)
{
if(q.size() == k && num < q.top())
continue;
q.push(num);
if (q.size() > k)
q.pop();
}
return q.top();
}
};
class Solution2 {
public:
int findKthLargest(vector<int>& nums, int k) {
//基本思想:手動實現堆排序,構造大頂堆,因爲大頂堆最後得到的是從小到大的順序
//構造大頂堆後,從堆尾先得到最大元素依次得到第k大元素即可
HeapSort(nums, k);
return nums[nums.size() - k];
}
//調整大頂堆,保持節點nums[start]大於孩子節點,調整範圍在[start+1,end]之間
void HeapAdjust(vector<int>& nums, int start, int end)
{
int temp = nums[start]; //保存nums[start]值
int i = 2 * start + 1; //start的一個孩子節點nums[2*start+1]另一個是nums[2*start+2]
while (i <= end)
{
//對於堆節點nums[start]進行調整,若小於兩個孩子節點nums[i]nums[i+1]中最大的則調整,否則break
if (i + 1 <= end && nums[i] < nums[i + 1])
i = i + 1;
if (temp > nums[i])
break;
nums[start] = nums[i]; //nums[start]大於兩個孩子節點中最小的,調整
start = i; //看調整後start的孩子節點i是否大於i的孩子節點,因此更新start和i
i = 2 * start + 1;
}
nums[start] = temp;
}
void HeapSort(vector<int>& nums,int k)
{
//堆排序第一步建立大頂堆,從下面節點往上面節點建立,就是保證當前節點i值大於兩個孩子節點2*i+1和2*i+1值
for (int i = nums.size() / 2 - 1; i >= 0; i--)
{
HeapAdjust(nums, i, nums.size() - 1);
}
//堆排序第二步依次交換堆頂和堆底, 這樣堆底就是排序後的最大值,該節點也去除堆中,大頂堆被破壞重新建立堆
int i = nums.size() - 1;
while(k--)
{
swap(nums[0], nums[i]);
HeapAdjust(nums, 0, i - 1);
i--;
}
}
};
int main()
{
Solution2 solute;
vector<int> nums = { 3,2,3,1,2,4,5,5,6 };
int k = 4;
cout << solute.findKthLargest(nums,k) << endl;
return 0;
}