Leetcode.0215 | 数组中的第K个最大元素

题目

给定整数数组 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(nlogk),其中遍历数据 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];
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值