寻找第 K 大的数

寻找第 K 大的数

1、参考资料

https://leetcode-cn.com/problems/kth-largest-element-in-an-array/

https://www.jianshu.com/p/33ee33ce8699

2、题目要求

题目描述

在未排序的数组中找到第 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 总是有效的,且 1 ≤ k ≤ 数组的长度。

3、代码思路

缩减版的快排

  1. 刚开始肯定会想到使用快排,先对 nums 数组进行快速排序,然后再返回第 k 大的数: nums[index],其中 index 为倒数第 k 个元素的下标:index=nums.length-k
  2. 仔细想想,因为我们只需要求取第 k 的数,其实完全不用对整个数组进行快排,可以使用阉割版的快排
  3. 我们调用 partition = int partition(int[] arr, int left, int right) { 函数将数组分为左右两半,其实就已经可以排除掉一部分元素,将不需要进行排序的数组区间剔除

剔除无需排序的区间:我们需要判断 partitionindex的关系

  1. 调用 partition() 函数后,在 partition 左边的元素都小于等于 arr[partition],在 partition 右边的元素都大于等于 arr[partition]
  2. 如果 partition = index,恭喜,运气不错,说明第 k 大的元素恰好就在 partition 处,已经找到第 k 大的元素,结束递归
  3. 如果 partition > index,则说明第 k 大的元素在 [partition+1, right] 之间,对该区间进行递归快排
  4. 如果 partition < index,则说明第 k 大的元素在 [left, partition-1] 之间,对该区间进行递归快排

时间复杂度近似为 O(n)

4、代码实现

代码:寻找第 K 大的数

class Solution {
    public int findKthLargest(int[] nums, int k) {
        quickSortK(nums, 0, nums.length - 1, nums.length - k);
        return nums[nums.length - k];
    }

    private void quickSortK(int[] arr, int left, int right, int index) {
        // 如果 left >= right,则证明此条路径的递归快排完成
        if (left < right) {
            // 将数组分为左右两半,partition 就是中间的分界线
            int partition = partition(arr, left, right);
            if (partition == index) {
                // 如果 partition == index,则说明 partition 就是第 k 大的元素
                return;
            } else if (partition > index) {
                // 如果 partition > index,则说明第 k 大的元素在区间左边,在左半区间递归进行快排
                quickSortK(arr, left, partition - 1, index);
            } else {
                // 如果 partition < index,则说明第 k 大的元素在区间右边,在右半区间递归进行快排
                quickSortK(arr, partition + 1, right, index);
            }
        }
    }

    // 将数组分为左右两半
    private int partition(int[] arr, int left, int right) {
        int pivot = arr[left];
        //终止while循环以后left和right一定相等的
        while (left < right) {
            while (left < right && arr[right] >= pivot) {
                --right;
            }
            arr[left] = arr[right];
            while (left < right && arr[left] <= pivot) {
                ++left;
            }
            arr[right] = arr[left];
        }
        arr[left] = pivot;
        //left可以改为right
        return left;
    }
}

下面是快排的代码,来和原版的快排对比一下

private static void quickSort(int[] arr, int left, int right) {
    if (left < right) {
        int partitionIndex = partition(arr, left, right);
        quickSort(arr, left, partitionIndex - 1);
        quickSort(arr, partitionIndex + 1, right);
    }
}

private static int partition(int[] arr, int left, int right) {
    int pivot = arr[left];
    //终止while循环以后left和right一定相等的
    while (left < right) {
        while (left < right && arr[right] >= pivot) {
            --right;
        }
        arr[left] = arr[right];
        while (left < right && arr[left] <= pivot) {
            ++left;
        }
        arr[right] = arr[left];
    }
    arr[left] = pivot;
    //right可以改为left
    return left;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值