题目
给定整数数组 nums
和整数 k
,请返回数组中第 k
个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
示例
输入:[3,2,1,5,6,4] 和 k = 2
输出: 5
输入:[3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
解决方法
使用堆排序解决此问题。首先在原地建立一个,做 k - 1次删除操作后堆顶元素就是我们要找的答案。
根据初始数组,手动构建大根堆。大根堆的本质是一棵完全二叉树,所有的父结点都比它的孩子结点数值大。从初始数组开始,从下到上从右到左,以三个节点为一个单元进行构建。下图是示例:
实际代码实现过程中,交换节点的过程就是交换数组的元素。父节点的下标为i,左右孩子的下标分别为2i+1 和 2i+2。详细的过程见代码注释
这种做法时间复杂度为,其中遍历数据 O(N),堆内元素调整的复杂度为O(logK)。
空间复杂度:O(K)。
代码实现
class Solution {
public:
void buildMaxHeap(vector<int>& a, int heapSize) {
//在nums中的前heapSize个元素中构建大顶堆
for (int i = heapSize / 2; i >= 0; --i) {
maxHeap(a, i, heapSize); //i为子树的根节点,从右下到左上,以子树为单位进行调整
}
}
//maxHeap负责对每个以i为根节点的子树进行调整,详细过程如上图所示。
void maxHeap(vector<int>& a,int i,int heapSize){ //在nums中的前heapSize个元素中构建大顶堆,对当前子树进行操作,父节点在数组nums中的下标为i
int left = 2*i+1;
int right = 2*i+2;
int largest = i;//先假设父节点是最大的
//寻找子树三个节点中最大的,记录其在数组中的下标位置
if (left < heapSize && a[left] > a[largest]) {
largest = left;
}
if (right < heapSize && a[right] > a[largest]) {
largest = right;
}
//如果左右节点比父节点大
if (largest != i) {
//将大节点换到父节点的位置上,原来的父节点被置换到了原来大节点的位置largest上。
swap(a[i], a[largest]);
//对置换后的子树进行调整,子树的根节点就是置换前原来的父节点。
maxHeap(a, largest, heapSize);
}
}
int findKthLargest(vector<int>& nums, int k) {
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
//对大根堆做k-1次删除操作,每次都删去堆顶,即nums[0]
for (int i = nums.size() - 1; i >= nums.size() - k + 1; --i) {
//将nums[0]调到后面
swap(nums[0], nums[i]);
--heapSize;
//对堆重新调整,忽略掉被删掉的老nums[0]
maxHeap(nums, 0, heapSize);
}
return nums[0];
}
};