剑指Offer17

剑指Offer第十七天

大堆顶排序(中等)

在这里插入图片描述

题1:最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
在这里插入图片描述

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        //用Arrays.sort()固然简单,但是这题的核心还是要手写快排吧。
        if(k==0 || arr.length == 0){
            return new int[0];
        }
        quickSort(arr, 0, arr.length-1);
        return Arrays.copyOf(arr, k);
    }

    private void quickSort(int[] arr, int left, int right){
        if(left >= right){
            return ;
        }
        //定义两个指针
        int i = left, j = right;
        while(i < j){
            //以基准数划分左右,左边为小于基准数的,右边为大于基准数的,让arr[left]作为基准数
            while(i<j && arr[j] >= arr[left]){
                j--;
            }
            while(i<j && arr[i] <= arr[left]){
                i++;
            }
            swap(arr, i,j);
        }
        //将基准数放在中间,将一个数组划分成两个数组
        swap(arr,i,left);

        //递归
        quickSort(arr, left, i-1);
        quickSort(arr, i+1, right);
    }
    private void swap(int[] arr, int a, int b){
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}


//快排就是,确定一个基准点,比如arr[left],然后从后面right开始比较,如果有小于这个基准点的,就把left位置的值替换成小于基准点的值,然后基准点备份原先left位置的值,然后在从前面找到一个值大于基准点,用于替换right位置的值,这样子一直换换换。。直到i==j
//这时候,相当于在i这个位置,把数组分成两部分,左边是小于基准点的,右边是大于基准点的
//所以分成两批就可以采用分治,进行处理(递归。。)

在这里插入图片描述

Tips:这题更好的做法是大根堆

// 保持堆的大小为K,然后遍历数组中的数字,遍历的时候做如下判断:
// 1. 若目前堆的大小小于K,将当前数字放入堆中。
// 2. 否则判断当前数字与大根堆堆顶元素的大小关系,如果当前数字比大根堆堆顶还大,这个数就直接跳过;
//    反之如果当前数字比大根堆堆顶小,先poll掉堆顶,再将该数字放入堆中。
class Solution {
 public int[] getLeastNumbers(int[] arr, int k) {
     if (k == 0 || arr.length == 0) {
         return new int[0];
     }
     // 默认是小根堆,实现大根堆需要重写一下比较器。
     Queue<Integer> pq = new PriorityQueue<>((v1, v2) -> v2 - v1);
     for (int num: arr) {
         if (pq.size() < k) {
             pq.offer(num);
         } else if (num < pq.peek()) {
             pq.poll();
             pq.offer(num);
         }
     }

     // 返回堆中的元素
     int[] res = new int[pq.size()];
     int idx = 0;
     for(int num: pq) {
         res[idx++] = num;
     }
     return res;
 }
}
题2:数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

  • void addNum(int num) 从数据流中添加一个整数到数据结构中。
  • double findMedian() 返回目前所有元素的中位数。

在这里插入图片描述

class MedianFinder {
    //声明大堆顶
    PriorityQueue<Integer> bigHeap;
    //声明小堆顶
    PriorityQueue<Integer> smallHeap;
    
    public MedianFinder() {
        //大堆顶初始化要重写比较器,因为队列默认实现是小堆顶
        //大堆顶的堆顶元素是最大值,越往下越拉,类似普通班
        bigHeap = new PriorityQueue<>((x,y)->(y-x));
        //小堆顶的堆顶元素是最小值,越往下越猛,类似实验班
        smallHeap = new PriorityQueue<>();
    }
    
    public void addNum(int num) {
        //实验班加进来一个人,然后重新排序,把最拉的人放在堆顶
        smallHeap.add(num);
        //普通版接收实验班最拉的进来,然后重新排队,把最猛的放在堆顶
        bigHeap.add(smallHeap.poll());
        //如果实验班人太少,就把普通版最猛的抽调过来
        if(smallHeap.size() + 1 < bigHeap.size()){
            smallHeap.add(bigHeap.poll());
        }
    }
    
    public double findMedian() {
        //普通班人数大于等于实验班,如果是奇数情况
        if(bigHeap.size()>smallHeap.size()){
            return bigHeap.peek();
        }
        //如果是偶数情况
        return (double)(smallHeap.peek() + bigHeap.peek())/2;
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值