堆排序
小顶堆解法
public class MaxNumK_215 {
/**
* 寻找数组中第K大的元素
*
* @param nums 数组
* @param k 第K大
* @return 第K大的元素
*/
public int findKthLargest(int[] nums, int k) {
int heapSize = nums.length;
// 构建大顶堆
buildHeap(nums, heapSize);
// 剔除k-1个根节点
for (int i = 0; i < k - 1; i++) {
nums[0] = nums[heapSize - 1];
--heapSize;
adjustHeap(nums, 0, heapSize);
}
// 返回第K个最大元素
return nums[0];
}
/**
* 构建大顶堆
*
* @param nums 数组
* @param heapSize 堆的大小
*/
private void buildHeap(int[] nums, int heapSize) {
for (int i = heapSize / 2 - 1; i >= 0; i--) {
adjustHeap(nums, i, heapSize);
}
}
/**
* 调整堆,使其满足大顶堆的性质
*
* @param nums 数组
* @param root 当前根节点的索引
* @param heapSize 堆的大小
*/
private void adjustHeap(int[] nums, int root, int heapSize) {
int maxIndex = root;
int lChild = root * 2 + 1;
int rChild = root * 2 + 2;
if (lChild < heapSize && nums[lChild] > nums[maxIndex]) {
maxIndex = lChild;
}
if (rChild < heapSize && nums[rChild] > nums[maxIndex]) {
maxIndex = rChild;
}
if (maxIndex != root) {
swapValue(nums, root, maxIndex);
adjustHeap(nums, maxIndex, heapSize);
}
}
/**
* 交换数组中两个元素的值
*
* @param nums 数组
* @param indexA 第一个元素的索引
* @param indexB 第二个元素的索引
*/
private void swapValue(int[] nums, int indexA, int indexB) {
int temp = nums[indexA];
nums[indexA] = nums[indexB];
nums[indexB] = temp;
}
}
整体复杂度:
时间复杂度:堆排序的时间复杂度为O(nlogn),其中n为数组的长度。建堆的时间复杂度为O(n),剔除根节点的操作需要进行k-1次,每次调整堆的时间复杂度为O(logn)。因此,堆排序的总体时间复杂度为O(n + (k-1)logn)。
空间复杂度:堆排序只需要使用常数级别的额外空间,因此空间复杂度为O(1)。
小顶堆解法
public class MaxNumK_V2 {
/**
* 寻找数组中第K大的元素
*
* @param nums 数组
* @param k 第K大
* @return 第K大的元素
*/
public static int findKthLargest(int[] nums, int k) {
int len = nums.length;
// 构建小顶堆
buildMinHeap(nums, k);
// 加入后续参数调整堆
for (int i = k; i < len; i++) {
if (nums[i] > nums[0]) {
nums[0] = nums[i];
adjustMinHeap(nums, k, 0);
}
}
return nums[0];
}
/**
* 构建小顶堆
*
* @param nums 数组
* @param heapSize 堆的大小
*/
private static void buildMinHeap(int[] nums, int heapSize) {
for (int i = heapSize / 2 - 1; i >= 0; i--) {
adjustMinHeap(nums, heapSize, i);
}
}
/**
* 调整小顶堆,使其满足小顶堆的性质
*
* @param nums 数组
* @param heapSize 堆的大小
* @param root 当前根节点的索引
*/
private static void adjustMinHeap(int[] nums, int heapSize, int root) {
int min = root;
int lChild = root * 2 + 1;
int rChild = root * 2 + 2;
if (lChild < heapSize && nums[lChild] < nums[min]) {
min = lChild;
}
if (rChild < heapSize && nums[rChild] < nums[min]) {
min = rChild;
}
if (min != root) {
swapValue(nums, min, root);
adjustMinHeap(nums, heapSize, min);
}
}
/**
* 交换数组中两个元素的值
*
* @param nums 数组
* @param indexA 第一个元素的索引
* @param indexB 第二个元素的索引
*/
private static void swapValue(int[] nums, int indexA, int indexB) {
int temp = nums[indexA];
nums[indexA] = nums[indexB];
nums[indexB] = temp;
}
}
整体复杂度:
时间复杂度:堆排序的时间复杂度为O(k + (n-k)logk),其中n为数组的长度。建堆的时间复杂度为O(k),剩余的元素进行插入调整堆的操作需要进行(n-k)次,每次调整堆的时间复杂度为O(logk)。因此,整体时间复杂度为O(k + (n-k)logk)。
空间复杂度:堆排序只需要使用常数级别的额外空间,因此空间复杂度为O(1)。