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

题目描述:

输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例 1:

输入:n = 12
输出:5
示例 2:

输入:n = 13
输出:6
 

限制:

1 <= n < 2^31

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:这道题没什么难的,就是麻烦,麻烦,麻烦。。。。而且可以说是一道找规律的题。我用到了递归。

先设定函数countbase,用来计算1,10,100,1000。。。即10的整数幂次中1的个数

例如,countbase(10)为0-9中1的个数,countbase(1)为1中的1个数。

由题意得,假定对于147来说,我们计算它的1的个数

num(147)= 1*countbase(100)+48+4*countbase(10)+10+countbase(1),

其中:

1*countbase(100):代表的是0-99中,所有个位,十位上1的个数

48:代表100-147中,百位上的48个1

4*countbase(10):代表的是0-9、10-19、20-29、30-39中,所有个位上(不包括十位)上1的个数

10:代表10-19中,十位上的10个1

countbase(1):代表的是40-47中,所有个位上1的个数

对于2444来说

num(2444)= 2*countbase(1000)+1000+4*countbase(100)+100+ 4*countbase(10)+10+ 1*countbase(1)

2*countbase(1000):代表0-999,1000-1999中,所有个位,十位,百位(不包括千位)上1的个数

1000:代表1000-1999中,千位上的1000个1

4*countbase(100):代表0-99,100-199,200-299,300-399中,所有个位,十位(不包括百位)上1的个数

100:代表100-199中,百位上的100个1

4*countbase(10):代表的是0-9、10-19、20-29、30-39中,所有个位上(不包括十位)上1的个数

10:代表10-19中,十位上的10个1

countbase(1):代表的是40-44中,所有个位上1的个数

这样的话,就能写出代码:

class Solution {
public:
    int countDigitOne(int n) 
    {
        // sum代表所有1的总和
        unsigned long long int sum=0;
        // res存储base中所有1的和
        // 以147为例,则res依次存储0-99,0-9的所有1的和
        // 以2444为例,则res依次存储0-999,0-99,0-9的所有1的和
        unsigned long long int res=0;
        // base从1开始,依次为10,100,1000。。。直到比n小的最大10的整数幂
        // 例如,n=147,那么base最大到100,n=2444,base最大到1000
        unsigned long long int base=1;
        // base<=n时循环
        while(base<=n)
        {
            // 在base没到达比n小的最大10的整数幂之前,用res记录base=1,10,100。。。中所有1的和
            res=countbase(base-1);
            
            if(n>=base&&n<base*10)
            {
                // base已经到最大了,开始计算有多少个base
                // 例如,n=147,则只有1个base,n=2444,则有2个base
                // left代表n的最高位
                int left=n/base;
                if(left>1)
                {
                    // 如果最高位>1,那么就有n个base的1要加上
                    res=res*left;
                    // 并且最高位上还有base个1
                    // 例如,n=2444,最高位left=2,base=1000,则2444之前,0-2000中含有两个base的1,而且在第二个base中,left所在的最高位,1出现了1000次(base次),即1000-1999中,千位上的1出现了1000次
                    res=res+base;
                    
                }
                else if(left==1)
                {
                    // 如果最高位left==1
                    res=res*left;
                    // right代表除了最高位的其他数字,例如n=147,left=1,right=47
                    unsigned long long int right;
                    if(n>=10)
                    right=n%base;
                    else
                    right=0;
                    // 加上最高位上right+1个数字1
                    // 例如n=147,要加上100-147中百位上的47+1=48个数字1
                    res=res+right+1;         
                }
                // sum加上res,保存好
                sum+=res;
                // 最高位处理完后,处理下一位。例如n=2444,千位上的1处理完之后,开始处理百位244中的数字1个数
                n=n%base;
                // base重新从1开始
                base=1;
                continue;
            }
            
            base=base*10;

        }
        return sum;
    }
    // 递归,来计算1,10,100,1000。。。。中的1的个数
    unsigned long long int countbase(unsigned long long int n)
    {
        if(n==0)
        return 0;
        if(n>=1&&n<=9)
            return 1;
        else
            return countbase(n/10)*10+n/10+1;
        return 0;
        
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值