本题来自:215. 数组中的第K个最大元素 - 力扣(LeetCode)
题面:
给定整数数组 nums
和整数 k
,请返回数组中第 k
个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
你必须设计并实现时间复杂度为 O(n)
的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4],
k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6],
k = 4
输出: 4
提示:
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104
代码:
void AdjustDown(int* MinHeap,int i,int k) // 节点下沉
{
int parent=i; // 父节点,也就是将要下沉的节点
int child=parent*2+1; // 子节点,父节点的孩子节点
while(child<k) // 如果孩子越界了,就不再比较
{
if((child+1 < k) && (MinHeap[child+1]<MinHeap[child])) // 如果右子节点小于左子节点
child++;
if(MinHeap[child]<MinHeap[parent]) // 如果子节点小于父节点
{
int tmp=MinHeap[child];
MinHeap[child]=MinHeap[parent];
MinHeap[parent]=tmp;
parent=child; // 父节点下沉,移动到子节点
child=parent*2+1; // 子节点下沉
}
else
{
break;
}
}
}
int findKthLargest(int* nums, int numsSize, int k)
{
int* MinHeap=(int *)malloc(sizeof(int)*k);
for(int i=0;i<k;i++)
{
MinHeap[i]=nums[i]; // 先把k个数放到堆中,不排序
}
for(int i=(k-1-1)/2;i>=0;i--)
{
AdjustDown(MinHeap,i,k); // 对堆内元素进行排序,除叶节点
}
for(int i=k;i<numsSize;i++)
{
if(MinHeap[0]<nums[i]) // 堆顶就是堆中最小的,如果nums的元素大于堆顶,就替换堆顶
{
MinHeap[0]=nums[i];
AdjustDown(MinHeap,0,k); // 替换之后下沉堆顶
}
}
return MinHeap[0]; // 堆顶就是第k个大的数
}
思路梳理:
已知堆排序的时间复杂度是O(N)。完美符合题意。
题中让找第k大的数,也就是从最大的数开始往后数,第k个数。利用了堆排序的思想。建立小根堆。
为什么是小根堆呢,因为堆里面最小的元素一直在堆顶,如果后进的元素比堆顶大,说明这个后进的元素一定是前k里面的数。也有可能这个数就是第k个数。建立小根堆,最小的一直在堆顶。堆里较大的数一直在堆底。较大的数一定比k大。只需要比较一次之后,就可以一直保存在堆里。
如果建立大根堆,堆顶一直是堆里面最大的数,那么是比堆顶大的进?还是比堆顶小的进?
万一堆顶一上来就是最大的数,那么所有的数都无法进堆了。如果是比堆顶小的,那么该不该进堆呢?
我们先建立这个堆,第一个循环就是把nums里面的前k个数,拷贝到堆里面。
第二个循环就是对堆进行排序。最后堆顶就是堆里面最小的数。
第三个循环是将剩余的nums的数与堆顶比较,如果比堆顶大,就替换堆顶。然后再对堆顶下沉。下沉到合适的位置。
最后把堆顶打出。