leetcode
leetcode 201 ~ 250
数字 1 的个数

数字 1 的个数

难度:

标签:

题目描述

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

 

示例 1:

输入:n = 13
输出:6

示例 2:

输入:n = 0
输出:0

 

提示:

  • 0 <= n <= 109

代码结果

运行时间: 18 ms, 内存: 16.1 MB


/*
 * Solution Idea using Java Streams:
 * We can utilize Java Streams to streamline the process of counting '1's. By creating a range
 * from 0 to n, we can use map and filter operations to count the occurrences of '1'. This
 * approach provides a functional and concise way to implement the solution.
 */
 
import java.util.stream.IntStream;
 
public class Solution {
    public int countDigitOne(int n) {
        return IntStream.rangeClosed(0, n)
                        .map(Solution::countOnesInNumber)
                        .sum();
    }
 
    // Helper method to count the number of '1's in a single number
    private static int countOnesInNumber(int num) {
        int count = 0;
        while (num > 0) {
            if (num % 10 == 1) {
                count++;
            }
            num /= 10;
        }
        return count;
    }
}

解释

方法:

这个题解采用了逐位枚举的思路。对于给定的整数n,我们将其转为字符串s,然后从高位到低位逐个分析每一位上1出现的次数。对于第i位,我们将数字分为三部分:第i位之前的部分(记为pre)、第i位(记为now)以及第i位之后的部分(记为suf)。根据now的值,我们可以分三种情况统计第i位上1出现的次数:1)now=0时,ans+=pre*mul;2)now=1时,ans+=pre*mul+suf+1;3)now>1时,ans+=(pre+1)*mul。最后,将每一位统计的结果累加即可得到最终答案。

时间复杂度:

O(m),其中m为整数n的位数

空间复杂度:

O(m),其中m为整数n的位数

代码细节讲解

🦆
为什么在计算第i位上数字1的出现次数时,需要将数字分为pre、now和suf三部分?
在计算第i位上数字1的出现次数时,将数字分为pre、now和suf三部分有助于我们从不同的角度考虑这一位数的贡献。具体来说,pre代表第i位之前的数字,这影响了第i位上1可以出现的次数;now是当前考察的位数,它的具体值决定了后续计算的方式;suf则是第i位之后的数字,它的值决定了当now为1时,后续位置上可能出现的所有数字组合。这种分割使得我们可以精确计算每一位对总1的个数的贡献,从而逐位累加得到最终答案。
🦆
当now=1时,为什么ans的计算方式是`ans += pre * mul + suf + 1`?具体是如何得出这个公式的?
当now=1时,表示当前位(第i位)为1。此时,1的总出现次数由两部分组成:1) pre部分决定了前面可以形成多少个完整的100...000到199...999之间的数(即从100...000到199...999总共pre个这样的区间),每个区间贡献了mul个1;2) suf部分表示,当前位为1时,后面的数字可以取0到suf的所有值,这样就有suf+1种可能(包括0这一个数)。因此,总的1的个数就是前面的区间数乘以每个区间的1的个数加上后面的数的可能性,即pre * mul + suf + 1。
🦆
对于now=0和now>1的情况,算法如何确保它们的计算方法正确反映了数字1的出现次数?
对于now=0的情况,当前位为0意味着1只能出现在pre定义的范围内的数字中,且仅当pre形成的数字在前面时第i位才可能为1,因此这部分的贡献是pre * mul。对于now>1的情况,当前位大于1意味着无论suf是什么,当前位都能取到1,即从0到now-1的任何数,在now为1时,suf可以取0到999...(l-i-1个9),因此贡献是(pre+1) * mul。这样的计算确保了无论now的值是多少,第i位上1的出现次数都能被正确计算。
🦆
请解释变量`mul`在算法中的作用及其如何影响计算结果?
变量`mul`代表的是10的l-i-1次幂,其中l是数字n的位数,i是当前考察的位数的索引(从0开始)。`mul`实际上是一个权重,表示当第i位上的数字变化时,对整个数字表示的影响。例如,如果i是1(即第二位),mul就是10的l-2次幂,反映了在这一位上每变化一个单位,数字总体变化了10^(l-2)。这个权重帮助我们在计算1的出现次数时,正确计算出因当前位的变化引起的所有可能的1的数量。

相关问题

阶乘后的零

给定一个整数 n ,返回 n! 结果中尾随零的数量。

提示 n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1

 

示例 1:

输入:n = 3
输出:0
解释:3! = 6 ,不含尾随 0

示例 2:

输入:n = 5
输出:1
解释:5! = 120 ,有一个尾随 0

示例 3:

输入:n = 0
输出:0

 

提示:

  • 0 <= n <= 104

 

进阶:你可以设计并实现对数时间复杂度的算法来解决此问题吗?

范围内的数字计数

范围内的数字计数