面试题 32,从1到整数n中1出现的次数

我虽然想到要用统计去做,但是思路不够明晰,这里参考答案的思路,组织下。

思路:以n=36789为例。

我们把计算分为1-6789,和6790-36789两段。

为什么这么做?

因为6790-36789这一段中,低四位的每一位数字,都涵盖了从0到9的变化;这种变化,每经过10000就会出现一次。这个例子中,6790-16789,16790-26789,26790-36789,共出现3次。每次出现,低四位的每一位都是0-9都体验了一遍,因此1的个数为10^3。一共出现3次,所以6790-36789这一段中,低四位上的1 共出现 3*10^3次。

那么6790-56789这一段中,最高位上出现了多少次1?因为这个区间包含了10000-19999的整个区间,只有这个区间,最高位上有1,所以10^4次。如果不包含10000-19999这个区间,比如题目求的是1-16789,那么1在最高位上出现的次数,就是6790次。

因此,第二段6790-36789中,1共出现 3*10^3 + 10^4。

第一段1-6789,求法等同于1-36789,因此可以使用递归。

 

递归终止条件:输入为1-9,也就是说第二个数字是1位数,返回1。

伪代码:

int CountNumberOfOne(int number){
    if(number <= 0)
        return 0;

    char strN[30];
    sprintf(strN, "%d", number);    
    if(strlen(strN) == 1)
        return 1;
    
    char *subStrN = substr(strN, 1, strlen(strN)-1);
    int newNum = subStrN - '0';
    
    int highestDigital = strN[0] - '0';
    int numberOf1InSecPart = highestDigtal * pow(10, strlen(subStrN) - 1);
    int numberOf1InFirstPart = 0;
    if(highestDigtal > 1){
        numberOf1InFirstPart = pow(10, strlen(subStrN));
    }else{
        numberOf1InFirstPart = newNum + 1;
    }
    
    return CountNumberOfOne(newNum) + numberOf1InFirstPart + numberOf1InSecPart;
}

 

书上的代码实现,上面的思路实现在方法二部分。

// NumberOf1.cpp : Defines the entry point for the console application.
//

// 《剑指Offer——名企面试官精讲典型编程题》代码
// 著作权所有者:何海涛

#include "stdafx.h"
#include <string.h>
#include <stdlib.h>

// ====================方法一====================
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)
    {
        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(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;

    // 假设strN是"21345"
    // numFirstDigit是数字10000-19999的第一个位中1的数目
    int numFirstDigit = 0;
    if(first > 1)
        numFirstDigit = PowerBase10(length - 1);
    else if(first == 1)
        numFirstDigit = atoi(strN + 1) + 1;

    // numOtherDigits是01346-21345除了第一位之外的数位中1的数目
    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)
{
    int result = 1;
    for(unsigned int i = 0; i < n; ++ i)
        result *= 10;

    return result;
}

// ====================测试代码====================
void Test(char* testName, int n, int expected)
{
    if(testName != NULL)
        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()
{
    Test();

    return 0;
}

 

 

posted on 2014-01-27 01:29  Felix Fang  阅读(225)  评论(0)    收藏  举报

导航