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

 

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