题目
- 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
策略
- 将已给数组构建为最大堆(大顶堆,即堆顶的元素是最大的,左右子堆也满足这个条件)
- 堆顶删除k-1个元素
- 此时堆顶元素为数组中第k大的元素
代码
class Solution {
public int findKthLargest(int[] nums, int k) {
int heapSize = nums.length;
// 建立最大堆,建堆之后nums[0]是最大值
buildMaxHeap(nums, heapSize);
// 删掉最大的k-1个数,剩下的最大的即为所求
// 删除的方式是将nums[0]与数组最后一个数交换,然后heapSize--
for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
swap(nums, 0, i);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
public void buildMaxHeap(int[] nums, int heapSize) {
// 堆从0开始计算,这样与数组的下标一一对应,i的左孩子为2i+1,右孩子为2i+2
// 堆化过程是自底向上的,此时从第一个非叶节点开始堆化
for (int i = (heapSize / 2) - 1; i >= 0; --i) {
maxHeapify(nums, i, heapSize);
}
}
public void maxHeapify(int[] nums, int i, int heapSize) {
// 因为是自底向上的堆化,所以此时nums[i]的左右孩子已经是各自子堆的最大值,只需要将这三个值进行比较即可,
// 而当发生交换(即当前节点不是三个值中最大值时),假设与左孩子交换,那么就会改变左子堆的堆化状态,此时需要再对左子堆进行堆化
// 找到i的左右孩子,从0开始的
int left = i * 2 + 1, right = i * 2 + 2, largest = i;
// 先和左孩子比
if (left < heapSize && nums[left] > nums[largest]) {
largest = left;
}
// 较大的再跟右孩子比
if (right < heapSize && nums[right] > nums[largest]) {
largest = right;
}
// 发生了交换,则需要继续堆化替换的节点
if (largest != i) {
swap(nums, i, largest);
maxHeapify(nums, largest, heapSize);
}
}
public void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
算法复杂度
- 时间复杂度:O(nlogn),建堆的时间代价是 O(n),删除的总代价是 O(klogn),因为 k<n,故渐进时间复杂为 O(n+klogn)=O(nlogn)。
- 空间复杂度:O(logn), 即递归使用栈空间的空间代价。
拓展:BFPRT算法(loading...)