77-快速排序练习-LeetCode215数组中的第k个最大元素

文章介绍了如何在给定整数数组nums中找到第k个最大的元素,提出了三种方法:使用优先级队列(时间复杂度O(nlogk)),直接排序(时间复杂度O(nlogn)),以及基于快速排序的分区思想(时间复杂度O(n))。并提供了使用快速排序分区策略的Java代码实现。

题目

给定整数数组 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 <= 10^5
    -10^4 <= nums[i] <= 10^4


思路1

优先级队列,取出前k大个元素,第k大个元素就在最后。时间复杂度O(nlogk)。


思路2

直接将数组排序,第k大个元素索引就是n - k。时间复杂度O(nlogn)。


思路3

快排分区partition思想,当分区函数返回的索引值 = n - k,该分区元素就是要找的元素。时间复杂度O(n)。


代码

class Solution {
     /**
     * 快速排序的partition分区思想解决第k大元素问题
     * @param nums
     * @param k
     * @return
     */
    public int findKthLargest(int[] nums, int k) {
        //第k大元素的索引为n - k
        //[1,2,3,4,5]第2大元素4对应的索引为5 - 2 = 3
        return findKthLargestInternal(nums, 0, nums.length - 1, nums.length - k);
    }

    /**
     * 在nums[l...r]找到索引为k元素
     * @param nums
     * @param l
     * @param r
     * @param k
     * @return
     */
    private int findKthLargestInternal(int[] nums, int l, int r, int k) {
        if(l > r) {
            //空区间
            return -1;
        }
        int p = partition(nums, l, r);
        if(p == k) {
            //此时索引p对应的元素恰好就是要查找的元素
            return nums[p];
        } else if(k > p) {
            //在右半区间接着找
            return findKthLargestInternal(nums, p + 1, r, k);
        }
        //此时k < p,在左半区间找
        return findKthLargestInternal(nums, l, p - 1, k);
    }

    /**
     * 分区函数
     * @param nums
     * @param l
     * @param r
     * @return
     */
    private int partition(int[] nums, int l, int r) {
        //默认选择第一个元素作为分区点
        int v = nums[l];
        //i是当前处理的元素
        //arr[l + 1...j] < v
        //最开始没有元素 < v
        int j = l;
        //arr[j + 1...i) >= v也是空区间
        for (int i = l + 1; i <= r; i++) {
            if(nums[i] < v) {
                swap(nums, j + 1, i);
                j++;
            }
        }
        //j对应 <v 的最后一个元素
        swap(nums, l, j);
        return j;
    }

    /**
     * 交换三连操作
     * @param nums
     * @param i
     * @param j
     */
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值