[leetCode]剑指 Offer 43. 1~n整数中1出现的次数

在这里插入图片描述

递归

 直观思路是遍历每一个数字,对每一个数字采用“%”“/”的方法取个位判断是否为1然后累加。由于数字n有O(logn)为所以时间复杂度为O(nlogn)。应该考虑更快的算法。
 假设输入的数字n为21345,可以把它分为两部分:

  • 1~1345
  • 1346~21345

先来分析1345~21345这部分。从万位开始万位出现1的次数为10000,因为可取10000 ~ 19999,1出现了10000次。剩余4位1出现的次数又是多少呢?把1345 ~ 21345 再分为两部分:

  • 1346 ~ 11345
  • 11346 ~ 21345
    由排列组合可知这两部分剩下4为每一位都可以取1,取1后剩下的3位有0~9种取法所以一共有 2 ∗ C 4 1 ∗ 1 0 3 = 8000 2 * C_4^1 * 10^3 =8000 2C41103=8000种。

最后剩下的1~1345 可以递归求解。由于递归的次数为n的位数所以时间复杂度位O(logn)

class Solution {

    public int countDigitOne(int n) {
        if(n <=  0) return 0;
        // 为方便递归计算将数字转换为字符串
        String strN = String.valueOf(n);
        return countDigitOne(strN);
    }

    private int countDigitOne(String strN) {
        int len = strN.length();
        if(strN == null || len == 0) 
            return 0;
        // 第一位数字
        int first = strN.charAt(0) - '0';

        // strN只有1位,且为0
        if(len == 1 && first == 0)
            return 0;
        // strN只有一位1只能出现1次
        if(len == 1 && first > 0)
            return 1;
        // 假设strN是21345
        // numOfFirstDigit是第一位1出现的数目:10000~19999;
        int numOfFirstDigit = 0;
        if(first > 1)
            numOfFirstDigit = powerBase10(len - 1);
        else if(first == 1)
            // 最后要+1 因为first本身为1
            numOfFirstDigit = Integer.valueOf(strN.substring(1,len)) + 1;
        // numOfOtherDigits是1346~21345中除第一位外的数位中的数目
        int numOfOtherDigits = first * (len - 1) * powerBase10(len - 2);
        // 1~1345中的数目
        int numRecursive = countDigitOne(strN.substring(1,len));
        return numOfFirstDigit + numOfOtherDigits + numRecursive;
    }

    // 求10的n次幂
    private int powerBase10(int n) {
        int result = 1;
        for(int i = 0; i < n; ++i) {
            result *= 10;
        }
        return result;
    }
}
posted @ 2020-09-06 09:10  消灭猕猴桃  阅读(79)  评论(0编辑  收藏  举报