zoukankan      html  css  js  c++  java
  • 剑指 Offer 43. 1~n 整数中 1 出现的次数

    思路

    这题如果直接用暴力法,1~n逐一判断,每个数逐位判断需要O(L)的时间,其中L为n的位数,所以总的时间复杂度为O(L*n),这显然会超时。

    方法:逐位判断,找规律

    假设n是4位数abcd, 即n=abcd,从右往左逐位分析:
    - 对于n中的第4位数d:
        - 如果d ≥ 1,对于1~n中第4位数为1的数,则其他3位数可以为000~abc,共1+abc种情况,即(000~abc)1;
        - 如果d = 0, 对于1~n中第4位数为1的数,则其他3位数可以为000~abc-1,共1+abc-1种情况,即(000~abc-1)1。

    - 对于n中的第3位数c:
        - 如果c>1,对于1~n中第3位数为1的数,其他3位数可以是000~ab9,共有1+ab9种情况,即该数可以为(00~ab)1(0~9);
        - 如果c=1,对于1~n中第3位数为1的数,其他3位数可以是000~abd,共有1+abd种情况,即该数可以为(00~ab)1(0~d);
        - 如果c=0,对于1~n中第3位数为1的数,其他3位数可以是000~(ab-1)9,共有1+(ab-1)9种情况,即该数可以为(00~ab-1)1(0~9)。
        
    - 对于n中的第2位数b:
        - 如果b>1,对于1~n中第2位数为1的数,其他3位数可以是000~a99,共有1+a99种情况,即该数可以为(0~a)1(00~99);
        - 如果b=1,对于1~n中第2位数为1的数,其他3位数可以是000~acd,共有1+acd种情况,即该数可以为(0~a)1(00~cd);
        - 如果b=0,对于1~n中第2位数为1的数,其他3位数可以是000~(a-1)99,共有1+(a-1)99种情况,即该数可以为(0~a-1)1(00~99)。
        
    - 对于n中的第1位数a:
        - 如果a>1,对于1~n中第1位数为1的数,其他3位数可以是000~999,共有1+999种情况,即该数可以为1(000~999);
        - 如果a=1,对于1~n中第1位数为1的数,其他3位数可以是000~bcd,共有1+bcd种情况,即该数可以为1(000~bcd);

    以上只是拿4位数举例子,无论n是几位数,规律都是和上面一样,可以依此法编写代码。
     

    举例:

        拿21058举个例子,从右往左逐位分析:
        对于第5位8:
            对于1~n中第5位数为1的数,左边的四位可以是0000~2105,共有2106种情况,即该数可以为(0~2105)1
        对于第4位5:
            对于1~n中第4位数为1的数,剩余的四位可以是0000~2109,共有2110种情况,即该数可以为(0~210)1(0~9)
        对于第3位0:
            对于1~n中第3位数为1的数,剩余的四位可以是0000~2099,共有2100种情况,即该数可以为(00~20)1(00~99)
        对于第2位1:
            对于1~n中第2位数为1的数,剩余的四位可以是0000~2058,共有2059种情况,即该数可以为(0~2)1(000~058)
        对于第1位2:
            对于1~n中第1位数为1的数,剩余的四位可以是0000~9999,共有10000种情况,即该数可以为1(0000~9999)
        因此,共有2106+2110+2100+2059+10000 = 18375种情况。
     
    注意:这种方法并没有漏算,比如110中有两个1,当判断到第2位的时候,第1位的1被算进去了,判断到第1位的时候,第2位的1也被算进去了,所以110中计算了两种情况,这和题目是相符的。

    复杂度分析

    时间复杂度:O(L2),其中L为数字n的长度(位数),在此处 L= log10n

    空间复杂度:O(1)

     
    根据以上分析,可以写出以下代码: 
     1 class Solution {
     2 public:
     3     int countDigitOne(int n) {
     4         int res = 0;
     5         int x = n;
     6         
     7         //首先计算n的总位数len
     8         int len = 0;
     9         while(x) {
    10             len++;
    11             x /= 10;
    12         }
    13 
    14         x = n;  // 假设n是4位数abcd, x=n=abcd
    15         
    16         for(int i = len; i >= 1; --i) {
    17             int m = x % 10; //m为当前第i位数的值
    18             x /= 10;        //x为当前第i位的左侧数字的大小(也就是n的前i位数的大小)
    19             if(i == len) {
    20                 if(m >= 1) {
    21                     res += 1 + x;
    22                 } else {
    23                     res += 1 + (x-1);
    24                 }
    25             } else {
    26                 if(m > 1) {
    27                     int rightDigitNum = len-i;
    28                     int t = x;
    29                     for(int j = 1; j <= rightDigitNum; ++j) {
    30                         t = t*10 + 9;
    31                     }
    32 
    33                     res += 1 + t;
    34                 } else if(m == 1) {
    35                     int t = x;
    36                     int r = 1;
    37                     int rightDigitNum = len-i;
    38                     for(int j = 1; j <= rightDigitNum; ++j) {
    39                         r *= 10;
    40                         t *= 10;
    41                     }
    42 
    43                     //n % r表示当前位右侧所有数字的大小
    44                     //最终算出来的t表示数字n去掉第i位数之后,剩余的数字的大小
    45                     t +=  n % r;    
    46                     res += 1 + t;
    47 
    48                 } else {    // m == 0
    49                     int rightDigitNum = len-i;
    50                     int t = x-1;
    51                     for(int j = 1; j <= rightDigitNum; ++j) {
    52                         t = t*10 + 9;
    53                     }
    54 
    55                     res += 1 + t;
    56 
    57                 }
    58             }
    59         }
    60         return res;
    61     }
    62 
    63 };

  • 相关阅读:
    Vue优化首页加载速度 CDN引入
    vue中前进刷新、后退缓存用户浏览数据和浏览位置的实践
    node.js
    keep-alive前进没有刷新
    移动端ios和安卓input问题
    前端技术原理
    Vue给子组件传值为空
    使用vue开发输入型组件更好的一种解决方式(子组件向父组件传值,基于2.2.0)
    Vue路由参数设置可有可无
    Vue组件的三种调用方式
  • 原文地址:https://www.cnblogs.com/FengZeng666/p/13940825.html
Copyright © 2011-2022 走看看