剑指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位),总共有以下几种情况:
- cur = 0 时
如 1203,当digit=10, cur=0,high=12,low=3时:
1~1203中使十位为1的数:0010 ~ 1119
拿去十位:000 ~ 119,共120个数,即high*digit个 - cur = 1 时
如 1213,当digit=10,cur=1, high=12,low=3时:
1~1213中使十位为1的数:0010 ~ 1211
拿去十位:000 ~ 121,共112个数,即 high*digit + low + 1个 - cur = 2~9 时
如1233,当digit=10,cur=3, high=12,low=3时:
1~1233中使十位为1的数:0010 ~ 1219
拿去十位:000 ~ 129,共130个数,即 (high+1)*digit 个
算法:
- 初始化digit=1,low=0,high=n/10, cur=n%10
- 根据cur的值进行上述计数
- 把cur加入low:low = cur*digit + low
cur改为high的最低位:cur = high % 10
high减去最低位:high = high / 10
位数升高:digit *= 10 - 重复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)