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

力扣

题意

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

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

解题思路

将 1 ~ n 的个位、十位、百位、...的 1 出现次数相加,即为 1 出现的总次数。

例如:n=3101592,那么我们要求的就是从3101592、3101591 .... 1,这些所有数字中,1在个位数字上出现的次数+1在十位上出现的次数+.....。

比如我们现在要求1在百位上出现的次数,首先我们定义一个变量 base=100,然后让当前在百位上的数字5记做cur,而在百位之前的数字记为high,百位之后的数字记作low。

我们发现,high=n/base/10;low=n%base;cur=(n/base)%10。

假设我们将百位上的数字cur定位1,那么它出现的次数等于 在它前面可能出现的数字的个数*在它后面可能出现数字的个数。

在本例中,在cur前面出现数字的范围为:0000~3101,而在它后面的数字变化范围是 00~99。

我们发现cur前面部分的可能出现数字的个数恰好就是 high+1,而在它后面可能出现的数字的个数恰好就是(99-0+1)=100,即base。

因此,1在百位上出现的次数=(high+1)*base。

推论:

n=3101592。

  • 若我们求的是1在千位上出现的次数,此时cur=1,而它前后出现的数字的个数情况有两种。
  1. high从000~309,low变化范围就是000~999
  2. high取310,low部分的变化范围就只能是000~592,因为此时如果low部分超过了592,整个的数字值就大于了给定的数字值。
  3. 因此在第一种情况下,cur前面部分可能的取值范围是(309-0+1=310),即high;而cur后面部分的取值范围是(999-0+1=1000),即base。
    1. 第二种情况下,cur前面部分部分出现的次数为1,后面部分取值范围是(592-0+1=593),即low+1。
  4. 所以,我们计算1在千位上出现的次数=(high*base)+(1*(low+1))
  • 求1在万位上出现的次数:
  1. 此时,cur=0
  2. cur前面部分可能出现的数字范围为:00~30,次数为(30-0+1),即high。
  3. cur后面部分可能出现的数字范围为:0000~9999,次数为(9999-0+1),即base。
  4. 所以,1在万位上出现的次数为:high*base。

 总结:

  • cur>1,我们返回的结果是:(high+1)*base
  • cur==1,我们返回的结果:high*base+low+1
  • cur==0,我们返回的结果是:high*base

C++实现

class Solution 
{
public:
    int countDigitOne(int n) 
    {
        //从个位开始,以此计算1在个位、十位、百位上出现的次数
        long base=1; //bae初始化为1,代表着从个位数上开始计算
        int res=0;  //最终的结果
        while(base<=n)
        {
            //计算cur之后的部分的数字low
            int low = n%base;

            //计算cur
            int high = n/base;
            int cur = high%10;

            //计算high
            high=high/10;

            if(cur>1)
            {
                res+=(high+1)*base;
            }
            else if(cur==1)
            {
                res+=(high*base+low+1);
            }
            else    //cur小于1
            {
                res+=high*base;
            }
            base*=10;   //更新base,计算上一个位数上1出现的次数
        }
        return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之所向便是光v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值