剑指 Offer-43 1~n整数中1出现的次数c++

-----------------------------------2021/1/25二刷---------------------------------------
具体每一位的思路并不难,只要能写出0的情况就能顺理成章写出1和2~9的情况,
难点在于边界判定和一系列数字的计算。
所以记录一下
边界: cur = 0且 hNum = 0单纯cur = 0绝对不行的:刚遇到hNum = 0说明进入最后一次计算。
数字变化:
一共有四个数字
lNum、hNum、cur、digit
变化顺序:
首先我们观察到cur = hNum /10, hNum = hNum /= 10
所以cur的变化必须在hNum之前。
然后lNum = lNum + cur * digit,digit = digit * 10
所以lNum的变化必须在cur和digit前
所以可得改变顺序:1. lNum 2.cur 3.digit和hNum随意

			lNum += cur * digital;
            //当前位置往前移动一位
            cur = hNum % 10;
            //截掉最低位
            hNum /= 10;
            //位权重乘10
            digital *= 10;	

------------------------------------一刷---------------------------------------------------

题目描述

在这里插入图片描述
一道找规律的困难题。一开始看到的时候,马上想当然0 ~ 9,10 ~ 99,100 ~ 999里面有几个1这样的思路去分析,但是这题的思路应该是对数字的每个位数来考虑出现1的次数
对于一个位数cur 设为第wCur位,其出现1的次数分为以下三种情况来讨论。

  1. cur为0
    我们设cur左边的位数构成的数字为 hNum10^wCur为digital
    wCur出现1的次数为 hNum * digital。
    eg 2205 ,cur = 0 ,wCur = 2 ,由于0 ~ 21 一共能给wCur位创造 21 - 0 + 1 次出现的机会;
    而wCur 本身作为第二位,对于每个机会能重复10^wCur次 比如对于第21次机会:2010 2011 2012 … 2019
  2. cur为1
    设cur左边的位数构成的数字为hNum,右边的位数构成的数字为lNum
    wCur出现1的次数为 hNum * digital+ lNum + 1
    eg 2215 ,cur = 1 , wCur = 2 , 除去第一种情况给予的机会;高位为 22 多创造的机会被重复的次数为 lNum + 1 即六次 :2210 2211 …2215
  3. cur大于1小于9
    我们设cur左边的位数构成的数字为 hNum,10^wCur为digital
    wCur出现1的次数为 hNum+1 * digital。
    eg 2225 , cur = 2 ,wCur = 2,0 ~ 22一共能给wCur位创造 22- 0 + 1 次出现的机会; 而wCur 本身作为第二位,对于每个机会能重复10^wCur次

那么接下来就是如何进入每一位从而得到各位出现1次数之和的过程。
我们从各位开始一直到最高位

初始值

hNum = n/10;
lNum = 0;
cur =  n%10;
digital = 1;

递归公式

//低位数字先计算当前位置数字大小
INum += cur * digital;
//当前位置往前移动一位
cur = hNum % 10 ;
//截掉最低位
hNum / =10//位权重乘10
digital *= 10

结束条件

hNum == 0 && cur == 0

这里为什么两者都要为0呢,可能有人会认为hNum为0即可,但是当我们处在最高位的时候hNum为0,同样需要再计算一次,当且仅当最后一次计算结束之后,两者才会都为0。

特殊情况

因为题目会到达int的上限值,所以digital在计算的过程中可能越界,应该把其数据类型设为long

得到代码

class Solution {
public:
    int countDigitOne(int n) {
        int hNum = n / 10;
        int lNum = 0;
        long digital = 1;
        int cur = n % 10;
        int ans = 0;
        int res;
        while(cur || hNum) {
            switch(cur) {
                case 0: res = hNum * digital;
                break;
                case 1: res = hNum * digital + lNum + 1;
                break;
                default:  res = (hNum + 1) * digital ;
                break;
            }
            ans += res;
            //低位数字先计算当前位置数字大小
            lNum += cur * digital;
            //当前位置往前移动一位
            cur = hNum % 10;
            //截掉最低位
            hNum /= 10;
            //位权重乘10
            digital *= 10;
        }
        return ans;
    }
    
};

在这里插入图片描述
时间复杂度O(logN)
空间复杂度O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值