CodeTop刷题Java——215.数组中的第K个最大元素

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

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

你必须设计并实现时间复杂度为O(n)的算法解决此问题。

解决方案

要求大小就要求排序,可以直接使用排序算法,但是这里要求O(n),一般的排序算法是达不到的。

方案一:手写一个大根堆

虽然jdk提供了PriorityQueue作为堆的实现,但是刷算法题直接调API也太low了。

但是这个方法内存占用太大了,毕竟堆是典型的空间换时间。

class Solution {
    public int findKthLargest(int[] nums, int k) {
        Heap heap = new Heap(nums.length);
        for (int num : nums) {
            heap.add(num);
        }
        int res = 0;
        // 一共弹出k次,最后一次弹出的就是需要的第k大的数
        for (int i = 0; i < k; i++) {
            res = heap.poll();
        }
        return res;
    }

    class Heap {
        // 存放数据的数组
        private int[] heapArr;
        // 堆的实时size(数组中有数据的位置是0~size-1)
        private int size = 0;
        // 堆的容量
        private int capacity;

        public Heap(int capacity) {
            this.capacity = capacity;
            this.heapArr = new int[capacity];
        }

        // 插入元素
        public void add(int num) {
            if(size == capacity) {
                throw new ArrayIndexOutOfBoundsException("heap容量已满");
            }
            // 先将值插入到数组中的下一个空闲位置
            heapArr[size++] = num;
            // 需要调整堆的结构
            insertHeapify(size-1);
        }

        // 弹出并删除顶部数据
        public int poll() {
            int res = heapArr[0];
            // 将堆中的最后一个节点放到堆顶
            heapArr[0] = heapArr[--size];
            // 调整堆的结构
            deleteHeapify();

            return res;
        }

        // 返回顶部数据(不做删除)
        public int peek() {
            return heapArr[0];
        }

        /**
        * 插入后进行调整
        */
        private void insertHeapify(int cur) {
            int parent = (cur - 1) / 2;
            /**
            * 后面一半的条件要看你要实现的时大根堆还是小根堆
            */
            while (parent >= 0 && heapArr[parent] < heapArr[cur]) {
                swap(parent, cur);
                cur = parent;
                parent = (cur - 1) / 2;
            }
        }

        /**
        * 该函数在末尾节点放到堆顶以后再调用,一定从堆顶开始
        */
        private void deleteHeapify() {
            int cur = 0;
            int leftChild = (2 * cur) + 1;
            int rightChild = (2 * cur) + 2;
            // (每次循环都只对以cur和它的左右节点进行操作)
            // 先判断有没有左孩子(右孩子的index一定比左孩子的index大1,如果左孩子都不存在右孩子就没有判断的必要)
            while (leftChild < size) {
                int maxChild = leftChild;
                // 如果右孩子存在并且还比左孩子大,就让右孩子成为maxChild
                if(rightChild < size && heapArr[rightChild] > heapArr[leftChild]) {
                    maxChild = rightChild;
                }

                // 上面的操作是在寻找最大的孩子,这里将父亲节点与最大的孩子进行比较
                // 只有父亲节点小于子节点才需要向下交换,如果父亲节点已经是最大的了就不需要再继续循环
                /** 当然这里是大根堆,如果是小根堆父亲节点大于子节点才向下交换 */
                if(heapArr[cur] < heapArr[maxChild]) {
                    swap(cur, maxChild);
                    cur = maxChild;
                    leftChild = (2 * cur) + 1;
                    rightChild = (2 * cur) + 2;
                } else {
                    break;
                }
            }
        }

        private void swap(int i, int j) {
            int temp = heapArr[i];
            heapArr[i] = heapArr[j];
            heapArr[j] = temp;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值