剑指offer——整数中1出现的次数(从1到n整数中1出现的次数)

题目描述

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

直观的解法:累加1到n之间的每个数中1出现的个数(每次求除以10的余数,除以10再求)。数n有logn位。

时间复杂度O(nlogn) 

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int number = 0;
        for(int i = 1; i <= n; i++)
        {
            number += NumberOf1(i);
        }
        return number;
    }
    int NumberOf1(int n)
    {
        int number = 0;
        while(n)
        {
            if(n % 10 == 1)
                number++;
            n /= 10;
        }
        return number;
    }
};

 

用一个大一点的数字21345作为例子来分析其中的数字规律。分成两段:1~1345,1346~21345。

先分析1346~21345段。最高位为1的情况有多少种呢?10000~19999(10^4个)。如果n的最高位为1,则只有10000~11345(除去最高位数字再加1种)。

再分析剩下4位中某位为1的情况:这里再将1346~21345分成两段1346~11345,11346~21345。每一段1的个数是:4*10^3 (其他3位可取0~9共10位数字中的任何一个)。

接下来递归分析1~1346中1个个数。

总递归次数等于n的位数。

时间复杂度O(logn)(底数是10)

#include<cstdio>
class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if(n <= 0) return 0;
        char strN[50];
        sprintf(strN, "%d", n);
        return NumberOf1(strN);
    }
    int NumberOf1(const char* strN)
    {
        if(!strN || *strN < '0' || *strN > '9' || *strN == '\0')
            return 0;
        int first = *strN - '0';
        unsigned int length = static_cast<unsigned int>(strlen(strN));
        if(length == 1 && first == 0)
            return 0;
        if(length == 1 && first > 0)
            return 1;
        
        int numFirstDigit = 0;
        if(first > 1)
            numFirstDigit = PowerBase10(length-1);
        else if(first == 1)
            numFirstDigit = atoi(strN+1) + 1;
        
        int numOtherDigits = first * (length - 1) * PowerBase10(length - 2);
        int numRecursive = NumberOf1(strN + 1);
        
        return (numFirstDigit + numOtherDigits + numRecursive);
    }
    
    int PowerBase10(unsigned int n)
    {
        int result = 1;
        for(unsigned int i = 0; i < n; i++)
            result *= 10;
        return result;
    }
};

 

其他人的思路 1

 

 

其他人的思路 2

 

public int NumberOfXBetween1AndN_Solution(int n,int x) {
    if(n<0||x<1||x>9)
        return 0;
    int high,low,curr,tmp,i = 1;
    high = n;
    int total = 0;
    while(high!=0){
        high = n/(int)Math.pow(10, i);// 获取第i位的高位
        tmp = n%(int)Math.pow(10, i);
        curr = tmp/(int)Math.pow(10, i-1);// 获取第i位
        low = tmp%(int)Math.pow(10, i-1);// 获取第i位的低位
        if(curr==x){
            total+= high*(int)Math.pow(10, i-1)+low+1;
        }else if(curr<x){
            total+=high*(int)Math.pow(10, i-1);
        }else{
            total+=(high+1)*(int)Math.pow(10, i-1);
        }
        i++;
    }
    return total;       
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值