【LeetCode】剑指 Offer 43. 1~n 整数中 1 出现的次数 p221 -- Java Version

题目链接:https://leetcode.cn/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/

1. 题目介绍(43. 1~n 整数中 1 出现的次数)

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

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

【测试用例】:
示例1:

输入:n = 12
输出:5

示例2:

输入:n = 13
输出:6

【条件约束】:

限制

  • 1 <= n < 2^31

【跟踪】:

注意:本题与主站 233. 数字 1 的个数 题目相同。

扫描二维码关注公众号,回复: 14731976 查看本文章

2. 题解

2.1 暴力枚举 – O(nlogn)

时间复杂度O(nlogn),空间复杂度O(1)

计算机解决问题其实没有任何奇技淫巧,它唯一的解决办法就是穷举,穷举所有可能性。算法设计无非就是先思考“如何穷举”,然后再追求“如何聪明地穷举”。

解题思路】:
累加 1~n 中每个整数 1 出现的次数。我们可以通过每次对 10 求余判断整数的个位数字是不是 1。如果这个数字大于 10,则除以 10 之后再判断个位数字是不是 1
……
实现策略】:

  1. 1 开始循环遍历到 输入的参数 n,将其放入对数求余方法中;
  2. 对数求余方法中定义变量 num,记录每个数字中 1 的数量并返回。
class Solution {
    
    
    // Solution1:枚举
    public int countDigitOne(int n) {
    
    
        int res = 0;
        // 枚举每一个数
        for (int i = 1; i <= n; i++) {
    
    
            res += NumberOf1(i);
        }
        // 循环结束,返回结果
        return res;
    }

    // 对数求余,判断该数有多少个1
    public int NumberOf1(int num) {
    
    
        int count = 0;
        while (num != 0){
    
    
            if (num % 10 == 1) count++;
            num = num/10;
        }
        return count;
    }
}

在这里插入图片描述

2.2 数学分析1(原书题解)-- O(logn)

时间复杂度O(logn),空间复杂度O(1)

解题思路】:
如果希望不用计算每个数字的 1 的个数,那就只能去寻找 1 在数字中出现的规律了。以数字 21345 为例,我们可以 1 ~ 21345 中的所有数字分为两段:

  • 一段是 1 ~ 1345
  • 一段是 1346 ~ 21345

……
其中 1346 ~ 21345 又可以分为两段:

  • 一段是 1346 ~ 11345
  • 一段是 11346 ~ 21345

……
根据以上的分段,我们就可以把求所有数字中 1 的个数分为 2 个区域:

  1. 仅首位是 1 的个数;
  2. 除去首位之后的数字的 1 的个数;

那么我们就可以使用递归的方法来解决这个问题,思路就是:每次去掉最高位进行递归,递归的次数和位数相同。以 21345 为例:

  1. 递归方法中要求 仅首位是 1 的个数(即,10000 ~ 19999 ),其个数为 Math.pow(10,len-1);
  2. 递归方法中要求 除去首位之后的数字的 1 的个数 (即,1346 ~x1345),其个数为 首位数字 * (位数-1) * Math.pow(10,len-2);
  3. 去掉首位后 (即,1 ~ 1345),递归。

……
【注意点】:

  • Math.pow(a,b) 的计算结果返回是double类型,double类型转换为int类型就需要用到类型转换: int c=(int)Math.pow(a,b) ;否则就会出现以下错误:
    在这里插入图片描述
class Solution {
    
    
    // Solution1:数学分析
    public int countDigitOne(int n) {
    
    
        // 无效数据判断
        if (n <= 0) return 0;
        // 将 n 转化为字符串(方便编程)
        String str = String.valueOf(n);

        return numberOf1(str);
    }

    // 分三部分判断 1~n 中 1 的个数
    public int numberOf1(String str) {
    
    
        // 获取字符数组长度(即,输入参数 n 的位数)
        int len = str.length();
        // 获取 n 中首位数字
        int first = str.charAt(0) - '0';
        // 如果长度为1,则根据情况返回 0 or 1
        if (len == 1 && first == 0) return 0;
        if (len == 1 && first > 0) return 1; 

        // 如果长度大于1,就对其进行数字划分,以21345为例
        // 第一区域:10000 ~ 19999 首位 1 的个数 (10000)
        int firstOneNum = 0;
        if (first > 1) firstOneNum = (int) Math.pow(10,len-1);
        // 如果首位为 1,那么首位 1 的个数就为后面的数字再加1
        else if (first == 1) firstOneNum = Integer.parseInt(str.substring(1))+1;

        // 第二区域:1346 ~ 21345 除首位之外所有 1 的个数
        int otherOneNum = first * (len-1) * (int) Math.pow(10,len-2);
        
        // 第三区域:1 ~ 1345 (递归求解) 
        int recursiveNum = 0;
        recursiveNum = numberOf1(str.substring(1));
        
        // 最后返回三个区域的加和 
        return firstOneNum + otherOneNum + recursiveNum;
    }
    
    // 返回 10 的 n 次幂
    public int powerBase10(int n) {
    
    
        int num = 1;
        for (int i = 0; i < n; i++) {
    
    
            num *= 10;
        }
        return num;
    }
}

在这里插入图片描述

2.3 数学分析2 – O(logn)

时间复杂度O(logn),空间复杂度O(1)

解题思路】:
某位中 1 出现次数的计算方法:
根据当前位 cur 值的不同,分为以下三种情况:

  • cur = 0
  • cur = 1
  • curr > 1
    在这里插入图片描述

其中 high 为高位,cur 为当前位,low 为 低位, digit 为 位因子

class Solution {
    
    
    public int countDigitOne(int n) {
    
    
        int digit = 1, res = 0;
        int high = n / 10, cur = n % 10, low = 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 /= 10;
            digit *= 10;
        }
        return res;
    }
}

在这里插入图片描述

3. 参考资料

[1] 面试题43. 1~n 整数中 1 出现的次数(清晰图解)
[2] Java中数字、字符、字符串 转换方法总结
[3] Java_String 截取字符串方法substring()
[4] 灵魂拷问:Java如何获取数组和字符串的长度?length还是length()?

猜你喜欢

转载自blog.csdn.net/qq_41071754/article/details/129896533