剑指offer 面试题43:1~n整数中1出现的次数 O(logn)时间(java)

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

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

思路

将n分为个位其他位

n = 4	5	6
		   个位

先分析个位出现1的次数:

  • 个位出现1的要取决于个位之前的几位,6之前是45,所以1至少会出现45次,因为个位是6,大于1,所以个位上还会出现一次1,总共出现45 + 1 = 46 次1

再分析十位

  • 因为个位从0~9经过一轮之后(总共变化10次 0,1,2…9),十位才会增加1,也就是说当十位上为1时,经过一轮变化,1会出现十次(10, 11, 12, … 19),十位上能出现几个1取决于十位之前的几位,上述例子中百位上是4,那么十位上至少会经过4轮变化,所以十位上的1会出现4次,4 * 10 = 40
  • 如果十位上原来的那个数(上述例子中是5)大于1,说明十位上的数在经过第5轮变化时(0,1,2,3,4,5)还会出现一次1,所以十位上总共出现5次1,1出现的个数为 4 * 10 + 1 * 10 = 50
  • 当十位上的数等于0时,说明十位上的数只会经过四轮变化 4 * 10 = 40
  • 当十位上的数等于1时,跟十位上的数大于1还有一些区别,此时需要看个位上的数值,比如416,十位数在经过第五轮变化时,只能有10, 11,12,13,14,15,16这几个数,此时1出现的个数为4 * 10 + 6 + 1 = 47

其他位同理

当前位之前更高位的数记为times,表示1至少出现的次数,比如456中当前位为个位时,times=45

参考这篇博客

class Solution {
    
    public int countDigitOne(int n) {
        if(n < 1) return 0;

        int count = 0;
        int base = 1; // base表示的是当前位的数值等于1时,1出现的次数,十位上为1,那么1可以出现10次(10, 11, ... 19)
        int times = n;

        while(times > 0) {
            int cur = times % 10; // cur代表当前位数字的数值   123当前位为1时,cur = 3
            times /= 10;
            count += times * base;
            if(cur == 1)
                count += (n % base) + 1;
            else if(cur > 1) count += base;
            base *= 10;
        }
        return count;
    }
}

时间复杂度

  • O(log n) ,因为要遍历数据n的每一位上的数

空间复杂度

  • O(1)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值