数组中第K大的数

329 阅读2分钟

题目描述

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4 输出: 4  

基于堆排

在小根堆的数据结构中,堆中的最小值总是位于根节点,在向堆中进行增减元素时,会动态调整对结构达到平衡,因此,我们可以利用这个性质,通过小根堆,来得到数组中第K个最大元素。

Java中可以使用优先级队列实现小根堆,定义该队列的大小为K,首先向其中加入K个元素,后续判断判断当前元素,是否大于堆顶值,如果大于,则移除堆顶,将当前值入堆,最后返回堆顶元素即可。

public int findKthLargest(int[] nums, int k) {
    PriorityQueue<Integer> heap = new PriorityQueue<Integer>(k);
    for(int i = 0;i < k;i++){
        heap.offer(nums[i]);
    }
    for(int i = k; i < nums.length; i++){
        if(nums[i] > heap.peek()){
            heap.poll();
            heap.offer(nums[i]);
        }
    }
    return heap.peek();
}

我们也可以建立一个大根堆,做 k − 1 次删除操作后堆顶元素就是我们要找的答案。在很多语言中,都有优先队列或者堆的的容器可以直接使用,但是在面试中,面试官更倾向于让更面试者自己实现一个堆。所以建议读者掌握这里大根堆的实现方法,在这道题中尤其要搞懂「建堆」、「调整」和「删除」的过程。

class Solution {
    Random rand=new Random();
    public int findKthLargest(int[] nums, int k) {
        return quickSort(nums,k,0,nums.length-1);
    }
    private int quickSort(int[] nums,int k,int left,int right){
        int index=rand.nextInt(right-left+1)+left;
        int flag=nums[index];
        nums[index]=nums[left];
        int i=left,j=right;
        while (i<j){
            while (i<j&&nums[j]<=flag) j--;
            nums[i]=nums[j];
            while (i<j&&nums[i]>=flag) i++;
            nums[j]=nums[i];
        }
        nums[i]=flag;
        if (i==k-1) return nums[i];
        else if (i<k-1) return quickSort(nums,k,i+1,right);
        else return quickSort(nums,k,left,i-1);
    }
}

:::caution 避免退化为链表 快速排序的性能和「划分」出的子数组的长度密切相关。所以我们一定要引入随机化,避免极端用例对算法复杂性的影响。

若无脑选择从nums[0]开始划分,那么假设原数组为 [5,4,3,2,1],使用快排查找第5大元素时,算法速度将提升为O(n). :::

TOP-K类题目思路

TOP-K问题,基本均可以通过堆和快排的思想来快速解决,需要注意的是,当使用堆解决时有时面试官可能更希望你自己实现一个堆,所以可以考虑直接拿出快排的方法来解决此问题。