中等题 数组中的第K个最大元素(堆排序)

本题来自: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的数与堆顶比较,如果比堆顶大,就替换堆顶。然后再对堆顶下沉。下沉到合适的位置。

最后把堆顶打出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值