寻找数组中第K大的元素

文章介绍了两种Java实现的堆排序算法,分别用于找到数组中的第K大元素:一种是基于大顶堆,另一种是基于小顶堆。这两种方法的时间复杂度和空间复杂度都进行了分析,大顶堆解法的时间复杂度为O(n+(k-1)logn),小顶堆解法的时间复杂度为O(k+(n-k)logk),两者空间复杂度均为O(1)。
摘要由CSDN通过智能技术生成

堆排序

小顶堆解法

public class MaxNumK_215 {

    /**
     * 寻找数组中第K大的元素
     *
     * @param nums 数组
     * @param k    第K大
     * @return 第K大的元素
     */
    public  int findKthLargest(int[] nums, int k) {
        int heapSize = nums.length;
        // 构建大顶堆
        buildHeap(nums, heapSize);
        // 剔除k-1个根节点
        for (int i = 0; i < k - 1; i++) {
            nums[0] = nums[heapSize - 1];
            --heapSize;
            adjustHeap(nums, 0, heapSize);
        }
        // 返回第K个最大元素
        return nums[0];
    }

    /**
     * 构建大顶堆
     *
     * @param nums     数组
     * @param heapSize 堆的大小
     */
    private  void buildHeap(int[] nums, int heapSize) {
        for (int i = heapSize / 2 - 1; i >= 0; i--) {
            adjustHeap(nums, i, heapSize);
        }
    }

    /**
     * 调整堆,使其满足大顶堆的性质
     *
     * @param nums     数组
     * @param root     当前根节点的索引
     * @param heapSize 堆的大小
     */
    private  void adjustHeap(int[] nums, int root, int heapSize) {
        int maxIndex = root;
        int lChild = root * 2 + 1;
        int rChild = root * 2 + 2;
        if (lChild < heapSize && nums[lChild] > nums[maxIndex]) {
            maxIndex = lChild;
        }
        if (rChild < heapSize && nums[rChild] > nums[maxIndex]) {
            maxIndex = rChild;
        }
        if (maxIndex != root) {
            swapValue(nums, root, maxIndex);
            adjustHeap(nums, maxIndex, heapSize);
        }
    }

    /**
     * 交换数组中两个元素的值
     *
     * @param nums    数组
     * @param indexA  第一个元素的索引
     * @param indexB  第二个元素的索引
     */
    private void swapValue(int[] nums, int indexA, int indexB) {
        int temp = nums[indexA];
        nums[indexA] = nums[indexB];
        nums[indexB] = temp;
    }
}

整体复杂度:

时间复杂度:堆排序的时间复杂度为O(nlogn),其中n为数组的长度。建堆的时间复杂度为O(n),剔除根节点的操作需要进行k-1次,每次调整堆的时间复杂度为O(logn)。因此,堆排序的总体时间复杂度为O(n + (k-1)logn)。
空间复杂度:堆排序只需要使用常数级别的额外空间,因此空间复杂度为O(1)。

小顶堆解法

public class MaxNumK_V2 {

    /**
     * 寻找数组中第K大的元素
     *
     * @param nums 数组
     * @param k    第K大
     * @return 第K大的元素
     */
    public static int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        // 构建小顶堆
        buildMinHeap(nums, k);
        // 加入后续参数调整堆
        for (int i = k; i < len; i++) {
            if (nums[i] > nums[0]) {
                nums[0] = nums[i];
                adjustMinHeap(nums, k, 0);
            }
        }
        return nums[0];
    }

    /**
     * 构建小顶堆
     *
     * @param nums     数组
     * @param heapSize 堆的大小
     */
    private static void buildMinHeap(int[] nums, int heapSize) {
        for (int i = heapSize / 2 - 1; i >= 0; i--) {
            adjustMinHeap(nums, heapSize, i);
        }
    }

    /**
     * 调整小顶堆,使其满足小顶堆的性质
     *
     * @param nums     数组
     * @param heapSize 堆的大小
     * @param root     当前根节点的索引
     */
    private static void adjustMinHeap(int[] nums, int heapSize, int root) {
        int min = root;
        int lChild = root * 2 + 1;
        int rChild = root * 2 + 2;
        if (lChild < heapSize && nums[lChild] < nums[min]) {
            min = lChild;
        }
        if (rChild < heapSize && nums[rChild] < nums[min]) {
            min = rChild;
        }
        if (min != root) {
            swapValue(nums, min, root);
            adjustMinHeap(nums, heapSize, min);
        }
    }

    /**
     * 交换数组中两个元素的值
     *
     * @param nums    数组
     * @param indexA  第一个元素的索引
     * @param indexB  第二个元素的索引
     */
    private static void swapValue(int[] nums, int indexA, int indexB) {
        int temp = nums[indexA];
        nums[indexA] = nums[indexB];
        nums[indexB] = temp;
    }
}

整体复杂度:

时间复杂度:堆排序的时间复杂度为O(k + (n-k)logk),其中n为数组的长度。建堆的时间复杂度为O(k),剩余的元素进行插入调整堆的操作需要进行(n-k)次,每次调整堆的时间复杂度为O(logk)。因此,整体时间复杂度为O(k + (n-k)logk)。
空间复杂度:堆排序只需要使用常数级别的额外空间,因此空间复杂度为O(1)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值