剑指offer T43 1-n中的整数的十进制中1出现的个数

博客介绍了计算1到n整数中1出现次数的方法,通过找规律分析高位、当前位和低位的影响。总结了根据当前位的取值推导出1出现在各位上的次数的公式,并提供了精确求解个位、十位、百位的技巧。
摘要由CSDN通过智能技术生成

case1:思想:找规律
可以这么做:依次计算1->n这个n个数中每位(个位、十位、百位…)上为1的数的总数
然后再把这些结果累加起来就是我们最终想要的结果。

具体做法:可以将n拆分成三个部分来看
high:n这个数比cur位 高的位的部分所表示的值
cur:表示当前计算的位上的值
low:n这个数比cur位 低的位所表示的值
比如:n=5014
假设当前计算的位为十位:
则把n拆为三个部分来看:
high = 50
cur = 1
low = 4
分析过程
假设我们对5014这个数字求解。
1->n中这n个数中:
(1)个位上为1数的个数:记高位为high=501,当前位为cur=4。
那么高位从0~500变化所产生的数中,每一个变化所产生的数中个位为1的数只出现1个,即(个位为1)这样的数字如5001,4991,4981…共501个;
高位是501时,因为当前位是4,所以此时对应个位为1的数只有1个即5011。
所以总共出现的次数为high1+1=502。
(2)十位1出现的个数:记高位high=50,当前位为cur=1,低位为low=4。
那么高位从0~ 49变化的过程中,每一个变化中1出现10次,即(高位10)~(高位19)这样的数字;
高位为50的时候,因为当前位是1,所以我们要看低位来决定出现的次数,因为低位为4,所以此时出现5次,即5010~5014这样的数字。
所以总共出现的次数为high
10+4=504。
(3)百位1出现的个数:记高位high=5,当前位cur=0,低位为low=14。
那么高位从0~ 4的过程中,每一个变化1出现100次,即(高位100)~(高位199)这样的数字;
高位为5的时候,因为当前位为0,所以不存在出现1的可能性。
所以总共出现的次数为high100+0=500。
(4)千位1出现的次数:记高位high=0,当前位cur=5,低位low=014。
那么因为没有高位所以直接看当前位,因为当前位为5,所以1出现的次数为1000,即1000~1999这样的数字。
所以总共出现的次数为high
1000+1000=1000。
综上,最终的结果将每个位置出现1的次数累加即可。

然后通过分析我们发现了一个规律:
可以根据n这个数cur位的取值情况来推出 1->n 这n个数中cur位为1的数的个数,推导公式为:
if(cur0)则从1->n这个n个数中这n个数中cur位为1的数的个数 = high*i(i表示的位数比如i表示个位则i = 1,i表示十位则i = 10以此类推)
else if(i
1)则从1->n这个n个数中这n个数中i位为1的数的个数 = highi+low+1
else 则从1->n这个n个数中这n个数中i位为1的数的个数 = high
i+i

【tips:】
1、如何精确求一个数的个为,十位,百位
令 i= 1,10,100,…10^n(表示位数,i=1表示个位,i=10表示十位…)
则 n/i 就表示仅保留n中i位及i位以上的数
2、由1我们可得可以这么拆分一个数n
high = n/(i*10)
cur = (n/i)%10
low = n - (n/i)*i

//case1
class Solution {
    public int countDigitOne(int n) {
      if(n<1)return 0;
      if(n<10) return 1;
      int res = 0;
      //i用于表示位数,i=1表示个位,=10表示十位
      //注意i要用long而不能用int,因为最多可以有31位,所以i最大可为10^31,超出int所能表示的范围;2^31-1
      long i = 1;
      //数n的第i位及i位以上的数不为0时
      while(n/i!=0){
         long high = n/(i*10); 
         long cur = (n/i)%10;
         long low = n-(n/i)*i;
         if(cur==0){
             res+=high*i;
         }else if(cur==1){
             res+=high*i+low+1;
         }else{
             res+=high*i+i;
         }
         //统计下一位上的情况
         i*=10;
      }
     return res;
    }
}

case2 思想:计算每个数个、十、百…位上为1的个数
//:暴力法直接超时

class Solution {
    public int countDigitOne(int n) {
      if(n<1)return 0;
      if(n<10) return 1;
      int res = 0;
      for(int i=n;i>0;i--){
        int temp = i;  
        while(temp>0){
          int rs = temp%10;
          if(rs==1){
              res++;
          }
          temp/=10;
        }
      }
      return res;
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值