《剑指offer》第四十三题:从1到n整数中1出现的次数

// 面试题43:从1到n整数中1出现的次数
// 题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如
// 输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。

#include <cstdio>
#include <cstring>
#include <cstdlib>

// ====================方法一====================
// 笨方法, 时间复杂度O(nlogn)
int NumberOf1(unsigned int n);

int NumberOf1Between1AndN_Solution1(unsigned int n)
{
    int number = 0;

    for (unsigned int i = 1; i <= n; ++i)
        number += NumberOf1(i);
    
    return number;
}

int NumberOf1(unsigned int n)
{
    int number = 0;
    while (n > 0)
    {
        if (n % 10 == 1)
            ++number;

        n = n / 10;
    }

    return number;
}

// ====================方法二====================
int NumberOf1(const char* strN);
int PowerBase10(unsigned int n);

int NumberOf1Between1AndN_Solution2(int n)
{
    if (n <= 0)
        return 0;

    char strN[50];
    sprintf_s(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)  //输入为 0
        return 0;
    if (length == 1 && first > 0)  //输入为 0~9
        return 1;

    // 假设strN是 21345
    //数字10000~19999中首位为1的数目
    int numFirstDigit = 0;
    if (first > 1)
        numFirstDigit = PowerBase10(length - 1);
    //首位数字为1, 如11345, 此时10000~11345
    else if (first == 1)  
        numFirstDigit = atoi(strN + 1) + 1; //则1345 + 1为首位为1的数目. atoi转换字符串为数字

    // numOtherDigits是01346-21345除了第一位之外的数位中1的数目
    //此时(length - 1)表示四个位置, (length - 2)表示其余三位可能的取值0~9
    int numOtherDigits = first * (length - 1) * PowerBase10(length - 2);

    // numRecursive是1-1345中1的数目
    int numRecursive = NumberOf1(strN + 1);

    return numFirstDigit + numOtherDigits + numRecursive;
}

int PowerBase10(unsigned int n)  //求10^n次方
{
    int result = 1;
    for (unsigned int i = 0; i < n; ++i)
        result *= 10;
    
    return result;
}
// ====================测试代码====================
void Test(const char* testName, int n, int expected)
{
    if (testName != nullptr)
        printf("%s begins: \n", testName);

    if (NumberOf1Between1AndN_Solution1(n) == expected)
        printf("Solution1 passed.\n");
    else
        printf("Solution1 failed.\n");

    if (NumberOf1Between1AndN_Solution2(n) == expected)
        printf("Solution2 passed.\n");
    else
        printf("Solution2 failed.\n");

    printf("\n");
}

void Test()
{
    Test("Test1", 1, 1);
    Test("Test2", 5, 1);
    Test("Test3", 10, 2);
    Test("Test4", 55, 16);
    Test("Test5", 99, 20);
    Test("Test6", 10000, 4001);
    Test("Test7", 21345, 18821);
    Test("Test8", 0, 0);
}

int main(int argc, char* argv[])
{
    Test();

    return 0;
}
测试代码

分析:第二种方法需要仔细分析规律。

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if (n <= 0)
            return 0;
        
        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 = n / 10;
        }
        return number;
    }
};
牛客网-方法一
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 numOtherDigir = first * (length - 1) * PowerBase10(length - 2);
        int numRecursive = NumberOf1(strN + 1);
        
        return numFirstDigit + numOtherDigir + numRecursive;
    }
    int PowerBase10(unsigned int n)
    {
        int result = 1;
        for (unsigned int i = 0; i < n; ++i)
            result *= 10;
        
        return result;
    }
};
牛客网-方法二

 

posted @ 2020-04-04 18:07  源周率  阅读(167)  评论(0编辑  收藏  举报