欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从1到n整数中1出现的次数 ----《剑指offer》面试题32

程序员文章站 2024-03-15 17:06:48
...

题目

输入一个整数N, 求从1到N这N个整数的十进制表示中,1出现的次数。例如:1-12中,1出现了: 1, 10 ,11,12 共5次

思路

一:暴力求解,从1遍历到n,求出每个数字1出现的次数
二:递归求解,以数字21345为例
 (1)将21345个数字分为两组。第一组为 1346~21345 共 20000个数字;第二组为 1 ~ 1345 共 1345个数字;
 (2)求第一组数字中,'1’出现的次数。
    ‘1’ 在万位出现的次数:10000~19999中,1总共出现了 10000次
         特殊情况:如果最位为1,则 ‘1’ 只出现: 12345 - 10000 + 1 = 2346次
   ‘1’出现在千位、百位、十位、个位的次数:
     千位: 用全排列方法,千位固定为1,万位可为1、2;百、十、个位可为0-9任意一个数字,所以总次数为:
        2 * 10 * 10 * 10 = 2000
     百位:同理 2000
     十位:同理 2000
     个位:同理 2000
 (3)用递归的方式,求剩下的 1345 个数字中 ‘1’ 出现的次数

代码

#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;


//  -----------------------1 暴力求解法

unsigned NumberOf1(unsigned n)
{
    unsigned count = 0;
    while (n)
    {
        if (n % 10 == 1)
            count++;
        n /= 10;
    }

    return count;
}

//  返回1到N每个数字中'1'出现的次数,方法一
unsigned NumberOfBetween1AndN(unsigned N)
{
    int count = 0;

    for (unsigned i = 1; i <= N; ++i)
        count += NumberOf1(i);

    return count;
}


//  -----------------------2 递归求解法

//  返回10的n次乘方
int PowerBase10(unsigned n)
{
    int result = 1;
    for (unsigned i = 0; i < n; ++i)
        result *= 10;

    return result;
}

//  返回1到N每个数字中'1'出现的次数,方法二
//  输入字符串格式的数字
int NumberOf1(const char* strN)
{
    //  检查输入合法性
    if (!strN || *strN < '0' || *strN > '9' || *strN == '\0')
        return 0;

    int first = *strN - '0';    //  数字的首位
    unsigned length = static_cast<unsigned >(strlen(strN));

    if (length == 1 && first == 0)
        return 0;

    if (length == 1 && first > 0)
        return 1;

    //  假设strN是“21345”
    //  numFisrtDigit是数字1在1346~21345这20000个数字中 万位为'1' 的数目
    int numFisrtDigit = 0;

    if (first > 1)
        numFisrtDigit = PowerBase10(length - 1);    // numFisrtDigit == 10000

    //  如果万位是以1开头,则万位为'1'的数量为: num - 10000 + 1 (num就是原本数字大小)
    else if (first == 1)
        numFisrtDigit = atoi(strN + 1) + 1;

    //  numOtherDigits是1346~21345这20000个数字中 除了万位为'1',其它位 为'1'的数目(xxxx1, xxx1x, xx1xx, x1xxx)
    int numOtherDigits = first * (length - 1) * PowerBase10(length - 2);    // numOtherDigits == 8000 = 2000 * 4

    //  numRecursive是1~1345 这剩下的1345个数字中 '1'出现的次数
    int numRecursive = NumberOf1(strN + 1);

    return numFisrtDigit + numOtherDigits + numRecursive;
}

int main()
{
    unsigned num = 100;
    printf("Method one: \n\t '1' occur %d times between 1 and %d\n", \
        NumberOfBetween1AndN(num), num);

    char strNum[] = "100";
    printf("Method two: \n\t '1' occur %d times between 1 and %s\n", \
        NumberOf1(strNum), strNum);

    return 0;
}