剑指Offer43 - 1~n整数中1出现的次数

力扣链接:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/

题目描述

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

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

 

思路:(数学规律)

题目要求我们统计n个整数中1出现的次数,我们反过来考虑,逐位统计当某为1时,可以产生多少个n以内的数。

由于有一位被固定为1,在统计个数时我们可以直接把这一位拿掉,将其前后两部分直接拼接起来,这样就成了计算连续的数字有多少个。

假设当前统计的位上的数字为cur,cur左边的数字组成的数为high,右边的为low,cur所在位为digit(表示10^digit位),总共有以下几种情况:

  1. cur = 0 时
    如 1203,当digit=10, cur=0,high=12,low=3时:
    1~1203中使十位为1的数:0010 ~ 1119
    拿去十位:000 ~ 119,共120个数,即high*digit个
  2. cur = 1 时
    如 1213,当digit=10,cur=1, high=12,low=3时:
    1~1213中使十位为1的数:0010 ~ 1211
    拿去十位:000 ~ 121,共112个数,即 high*digit + low + 1个
  3. cur = 2~9 时
    如1233,当digit=10,cur=3, high=12,low=3时:
    1~1233中使十位为1的数:0010 ~ 1219
    拿去十位:000 ~ 129,共130个数,即 (high+1)*digit 个

算法:

  1. 初始化digit=1,low=0,high=n/10, cur=n%10
  2. 根据cur的值进行上述计数
  3. 把cur加入low:low = cur*digit + low
    cur改为high的最低位:cur = high % 10
    high减去最低位:high = high / 10
    位数升高:digit *= 10
  4. 重复2,3步直到high和cur都为0,即所有位都被统计完了。

 

代码:

/**
 * @param {number} n
 * @return {number}
 */
var countDigitOne = function(n) {
    let digit = 1, high = Math.floor(n / 10), cur = n % 10, low = 0;
    let res = 0;
    
    while(!(high === 0 && cur === 0)){
        if(cur === 0){
            res += high * digit;
        }else if(cur === 1){
            res += high * digit + low + 1;
        }else{
            res += (high + 1) * digit;
        }
        
        low += cur * digit;
        cur = high % 10;
        high = Math.floor(high / 10);
        digit *= 10;
    }
    
    return res;
    
};

时间复杂度:O(logN) (N的位数)

空间复杂度:O(1)

 

posted @ 2020-07-10 00:39  studystudyxinxin  阅读(113)  评论(0编辑  收藏  举报