215數組中的第K個最大元素(堆排序)

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章