【刷题笔记N2】- 堆:数组第K个最大的数,数组中的中位数

刷题笔记系列

【刷题笔记01】- 数组
【刷题笔记02】-链表
【刷题笔记03 -哈希表】
【刷题笔记04】- 字符串
【刷题笔记05】-栈与队列
【刷题笔记06】树


1、 LeetCode215. 数组中的第K个最大元素

https://leetcode.cn/problems/kth-largest-element-in-an-array/description/
主要就是排序,然后取第K个最大元素。

  • 堆排序
    class HeapSort {
        public int findKthLargest(int[] nums, int k) {
            int heapSize = nums.length;
            buildMaxHeap(nums, heapSize);
            for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
                // swap并--heapSize后就相当于把大根堆的最大值移除了
                // 所以 i >= nums.length - k + 1, 移除k-1个后,就大根堆堆顶就是整个数组第k大的数了
                swap(nums, 0, i);
                --heapSize;
                // 为什么这里调整堆不是从下往上了呢?
                // 因为堆建好后已经确定父节点必然大于子节点,这里节点0是从堆底换上来的比较小的数,它拿上来只是起一个调整堆的作用
                maxHeapify(nums, 0, heapSize);
            }
            return nums[0];
        }

        public void buildMaxHeap(int[] a, int heapSize) {
            // 为什么是这样的访问顺序?
            // ① 逆序是因为要 避免 出现调整子树有序后,最值移到子树顶端并比其父节点(已访问)大的情况。逆序访问,即使调整后,子树顺序不对了,也有办法重新调整
            // ② i > heapSize/2 的都是叶子节点,无需考虑,所以起始 index 从 heapSize / 2开始
            for (int i = heapSize / 2; i >= 0; --i) {
                maxHeapify(a, i, heapSize);
            }
        }

        public void maxHeapify(int[] a, int i, int heapSize) {
            int lChildIndex = i * 2 + 1;
            int rChildIndex = i * 2 + 2;
            int largestIndex = i;
            // 注意两次都是和 a[largest]比较,不是和 a[i] 比,要选取三者中的最大
            if (lChildIndex < heapSize && a[lChildIndex] > a[largestIndex]) {
                largestIndex = lChildIndex;
            }
            if (rChildIndex < heapSize && a[rChildIndex] > a[largestIndex]) {
                largestIndex = rChildIndex;
            }
            if (largestIndex != i) {
                swap(a, i, largestIndex);
                // 交换后,调整和 i调换了最值的子树(因为可能a[i]比largest的孩子的值小
                maxHeapify(a, largestIndex, heapSize);
            }
        }

        public void swap(int[] a, int i, int j) {
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }
  • 快排
    public int findKthLargest(int[] nums, int k) {
        int target = nums.length - k;
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int index = quickSort(nums, left, right);
            if (index == target) {
                return nums[target];
            } else if (index < target) {
                left = index + 1;
            } else {
                right = index - 1;
            }
        }
        return -1;
    }

    public int quickSort(int[] array, int left, int right) {
        int pivot = array[left];
        int startIndex = left;
        left = left + 1;
        // 因为是和pivot比较,所以即使left right重合了也要进入循环处理这个数
        while (left <= right) {
            while (left <= right && array[left] < pivot) {
                left++;
            }
            while (left <= right && array[right] > pivot) {
                right--;
            }
            if (left >= right) break;
            swap(array, left, right);
            left++;
            right--;
        }
        // 最后一定是right在左 left在右,所以是交换nums[right]和 pivot
        swap(array, startIndex, right);
        return right;
    }

    public void swap(int[] array, int index1, int index2) {
        // 这种不用额外空间的交换方式必须 index1 与 index2不相等,不然就是在同一个地址进行加法操作,结果自然不正确。
        if (index1 == index2)
            return;
        array[index1] += array[index2];
        array[index2] = array[index1] - array[index2];
        array[index1] = array[index1] - array[index2];
    }

2、LeetCode295. 数据流的中位数

https://leetcode.cn/problems/find-median-from-data-stream/description/

class MedianFinder {
    // minQue 存放小于等于 中位数 的数据, 顺序
    Queue<Integer> minQue;
    // maxQue 存放大于 中位数 的数据, 逆序
    Queue<Integer> maxQue;

    public MedianFinder() {
        minQue = new PriorityQueue<>((a,b)->(a-b));
        maxQue = new PriorityQueue<>((a,b)->(b-a));
    }

    public void addNum(int num) {
        if (minQue.isEmpty() || num <= minQue.peek()){
            minQue.offer(num);
            if(minQue.size() - maxQue.size() > 1){
                minQue.offer(minQue.poll());
            }
        }else {
            maxQue.offer(num);
            if (maxQue.size() - minQue.size() > 0){
                maxQue.offer(maxQue.poll());
            }
        }
    }

    public double findMedian() {
        if (minQue.size() > maxQue.size()){
            return minQue.peek();
        }else {
            if (minQue.size() == 0)
                return 0;
            return (maxQue.peek() + minQue.peek())/2.0;
        }
    }
}
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值