剑指offer 32. 整数中 1 出现的个数 & leetcode 233. 数字 1 的个数
32. 整数中 1 出现的个数 & 233. 数字 1 的个数
题目描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
思路一:
思路参考:https://leetcode-cn.com/problems/number-of-digit-one/solution/shu-zi-1-de-ge-shu-by-leetcode/
在方法一中,我们手动计算了每个数中 ’1’ 的个数,但这种计算是非常慢。因此,我们需要找到’1’ 在这些数中出现的规律。然后我们就能利用这个规律来形成数学公式解决问题了。
考虑 ’1’ 在 个位,十位,百位,... 出现的情况,我们可以做出以下的分析
1 class Solution { 2 public int countDigitOne(int n) { 3 int count = 0; 4 for(long i = 1; i <= n; i *= 10){ 5 long divider = i * 10; 6 count += (n / divider) * i + Math.min(Math.max(n % divider - i + 1, 0L), i); 7 } 8 return count; 9 } 10 }
leetcode 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:35.1 MB, 在所有 Java 提交中击败了89.87%的用户
复杂度分析:
时间复杂度:迭代次数是 log10n, 所以时间复杂度为 O(log10n)。
空间复杂度:O(1)。
思路二:
从 1 - n, 分别统计每个数中1的个数
1 public class Solution { 2 public int NumberOf1Between1AndN_Solution(int n) { 3 // dp[i]表示 从 1 - i中所有数 中 1 的个数 4 // dp[i] + cnt; // cnt 表示当前数中1的个数 5 // 统计每个数中1的个数,右移一位,连续31次右移 6 7 int count = 0; 8 for(int i = 1; i <= n; i++){ 9 // 统计每个数中1的个数,右移一位,连续31次右移 10 int tmp = i; 11 while(tmp != 0){ 12 count += (tmp % 10 == 1) ? 1 : 0; 13 tmp /= 10; 14 } 15 } 16 return count; 17 } 18 }