从1到n的所有数相关问题

在学习过程中,遇到了两道题目。卡了自己一段时间,通过查阅资料,已经AC。下面贴一下这两道题目。

这道题是计算1~n的所有数字中,1出现的个数。这道题目是剑指offer中比较难的一道题目。首先考虑在计算过程中,有一定规律,但是在具体的coding过程中,总不能加各种if条件句判断吧。所以通过查资料学习了这样一种解决该问题的方法:

例如,n=abcde。首先,将n切分开来存放到数组之中。之后进行具体的逻辑判断。以具体数值c为例,需要考虑他的高位和低位情况,高位即ab的情况,低位即de的情况。

先不考虑c的具体值,先考虑c的高位的一个情况。00~ab-1,因为当考虑的ab时,需要看c的情况,所以先判断00~ab的一个情况,这时候其实可以直接算出来至少100种(00~99)之后需要考虑c的情况,如果c=0,则不考虑,如果c=1,则需要加上de+1(00~de)。如果c>1.则直接加100(00~99)。具体逻辑就是这样

/***
     * 设数字为abcde,当前位数为c位,
       c位1的个数即为高位个数+低位个数
       高位范围为 00 ~ ab-1 :
       有 ab*100 个(因为c在百位)
       低位分为三种情况:
       c = 0 ,有 0 个
       c = 1 ,有 de + 1 个
       c > 1 , 有 100 个 (因为c在百位)
       依次遍历每一位数相加,即为总共1的个数
     * @param n
     * @return
     */
    public static int numberOf1Between1AndN_Solution(int n) {
        if(n==0) {
            return 0;
        }
        ArrayList<Integer>list=new ArrayList<>();
        while(n!=0){
            list.add(n%10);
            n=n/10;
        }
        int size=list.size();

        int res=0;
        for (int i=size-1;i>=0;i--){
            int left=0,right=0,t=1;
            //处理第i位之前的数
            for (int j=size-1;j>i;j--){
                left=left*10+list.get(j);
            }
            //处理第i位之后的数
            for (int j=i-1;j>=0;j--) {
                right=right*10+list.get(j);
                t=t*10;
            }
            //00~ab-1的情况
            res+=left*t;
            if (list.get(i)==1) {
                res += right + 1;
            }else if (list.get(i)>1){
                res+=t;
            }
        }
        return res;
    }

 

第二道题目计算第n位数的具体数值。这道题目数学的味道比较浓厚。需要我们找一找固定的规律。

首先1位数有10中情况,2位数有90中情况,3位数有900种情况。。。

其次,我们需要根据n的具体值,来判断第n位数所在的值是多少位数所组成。所以,需要根据具体的规律算出来。例如,确定好n在3位数中的某一个值的某一位上。需要对ceil(n/3)确定在第几个数字上。之后需要n对i取模,计算在该值的第几位上。

我们需要设置一个初始变量 i代表是几位数的情况,s代表i位数有多少个,base代表i位数的起始值。

需要注意的是,为了便于计算,我们规定起始值从1开始,这样1位数有9种情况,2位数有90种情况,3位数有900中情况。。。便于计算。最终在计算第几个数时,减去1即可。

public int digitAtIndex(int n) {
        if (n==0) {
            return 0;
        }
        /***
         * i代表是几位数,base代表i位数时的起始值,s代表i位数的个数
         */
        long i=1,s=9,base=1;
        while (n>i*s){
            n= (int) (n-i*s);
            i+=1;
            base*=10;
            s=s*10;
        }
        /**
         * 剩下的n代表第i位后面的情况,number用于计算剩下的n应该在base的什么位置,n/i需要向上取整
         * 这里最后减1是因为需要考虑最开始的0.
         * r用于计算n具体的位置。
         */
        int number= (int) (base+(n+i-1)/i-1);
        int r= (int) (n%i==0?i:n%i);
        for (int j=0;j<i-r;j++){
            number/=10;
        }
        return number%10;
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值