假期无聊,不如一起刷《剑指offer》(第六天)

文章介绍了如何使用堆数据结构解决数据流中位数问题,通过维护两个大小根堆保持元素平衡以达到O(logN)的时间复杂度。同时,文章提到了计算n整数中1出现次数的规律,以及寻找数字序列中某一位数字的方法,涉及动态调整范围和计算逻辑。
摘要由CSDN通过智能技术生成

剑指 Offer 41. 数据流中的中位数

剑指 Offer 41. 数据流中的中位数
这道题是求数据流的中位数,一般情况我们可以采用排序的方式很轻松的找出中位数。如果我们采用插入排序的话,每次插入数字的时间复杂度大概是O(N),怎么能让这个时间更短呢?那么我们可以利用大小根堆的特点把单次查找的时间复杂度缩短到O(logN)。可以使用堆,具体做法如下:
我们把数据分为两部分,一部分是小于中位数的大根堆,另一部分是大于等于中位数的小根堆。我们控制两个堆的元素个数之差<=1,这样的话,如果两堆元素个数相同,就各取堆顶元素均分得到中位数,差1就取较多元素的堆顶。
具体如何插入,我们分类讨论:
在这里插入图片描述

class MedianFinder {
    private PriorityQueue<Integer> smallHeap;
    private PriorityQueue<Integer> bigHeap;
    /** initialize your data structure here. */
    public MedianFinder() {
        smallHeap=new PriorityQueue();//初始默认小根堆
        bigHeap=new PriorityQueue(new Comparator() {//转为大根堆
            @Override
            public int compare(Object o1, Object o2) {
                return (Integer)o2-(Integer)o1;
            }
        });
    }
    
    public void addNum(int num) {
        //保证size差值<=1
        if(smallHeap.size()>bigHeap.size()){//优先考虑往big插入
            if(num>smallHeap.peek()){//如果大于small的最小值,从small拿一个到big,num插入small
                int tmp=smallHeap.poll();
                bigHeap.add(tmp);
                smallHeap.add(num);
            }else{
                bigHeap.add(num);
            }
        }else if(smallHeap.size()<bigHeap.size()){//优先考虑往small插入
            if(num<=bigHeap.peek()){//如果小于等于小根堆的最小值,从big拿一个到small,num插入big
                int tmp=bigHeap.poll();
                smallHeap.add(tmp);
                bigHeap.add(num);
            }else{
                smallHeap.add(num);
            }
        }else{
            if(bigHeap.size()!=0&&smallHeap.size()!=0&&num>bigHeap.peek()){
                smallHeap.add(num);
            }else{
                bigHeap.add(num);
            }
        }
    }
    
    public double findMedian() {
        // if(smallHeap.size()!=0){
        //     System.out.println("a:"+smallHeap.peek());
        // }
        // if(bigHeap.size()!=0){
        //     System.out.println("b:"+bigHeap.peek());
        // }
        if(smallHeap.size()>bigHeap.size()){
            return 1.0*smallHeap.peek();
        }else if(smallHeap.size()<bigHeap.size()){
            return 1.0*bigHeap.peek();
        }else{
            return 1.0*(smallHeap.peek()+bigHeap.peek())/2;
        }
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

剑指 Offer 43. 1~n 整数中 1 出现的次数

剑指 Offer 43. 1~n 整数中 1 出现的次数
我首先的想法是正常循环,挨个数字判断,但是很明显没有这么简单:时间超限了
在这里插入图片描述
既然不能循环,我觉得应该是有什么规律可循。练习了两个半小时的斯某人,决定打开题解借鉴。
以下转自:leetcode题解

得到了规律:
根据当前位 cur值的不同,分为以下三种情况:
当cur=0 时: 此位 1的出现次数只由高位 high决定,计算公式为:
high×digit
当cur=1 时: 此位 1的出现次数由高位 high和低位决定,计算公式为:
high×digit+low-1
当cur=其他数字时: 此位 1的出现次数只由高位 high决定,计算公式为:
(high+1)×digit

class Solution {
    public int countDigitOne(int n) {
        int digit = 1, res = 0;
        int high = n / 10, cur = n % 10, low = 0;
        while(high != 0 || cur != 0) {
            if(cur == 0) res += high * digit;
            else if(cur == 1) res += high * digit + low + 1;
            else res += (high + 1) * digit;
            low += cur * digit;
            cur = high % 10;
            high /= 10;
            digit *= 10;
        }
        return res;
    }
}

剑指 Offer 44. 数字序列中某一位的数字

剑指 Offer 44. 数字序列中某一位的数字
在这里插入图片描述
因为right很容易越界,我们使用long类型

class Solution {
    public int findNthDigit(int n) {
        //先把范围缩小到0-9 或者10-99 或者100-999等等
        long k=1;
        long level=1;//1代表0-9  每+1,代表范围扩大 例如2代表10-99
        long chars=0;//目前已占用的字符数
        long right=10;//每个范围所占字符数
        while(n>right){
            level++;//范围增大
            chars=(int)right;
            right=right+(level*90*k);
            k*=10;
        }
        n-=chars;
        //保存除数的结果
        long div=n/level;
        //保存取模的结果
        long mod=n%level;
        //将除数加上所在范围的基数u,比如443 得到84要+100
        if(k!=1)
        div+=k;
        return Long.toString(div).charAt((int)mod)-'0';
    }
}

共勉
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值