從1到n整數中1出現的次數(比如121這一個整數裏就含有兩個1)

1、通過舉例來描述問題

【用例1】5

        答案:1。因爲只有1中含有一個。

【用例2】10

        答案:2。因爲只有1和10中各含有一個。

【用例3】55

        答案:16。因爲10~19中的十位各有一個;01、11、21、31、41、51的個位各有一個。    

【用例4】99

        答案:20。因爲10~19中的十位各有一個;01、11、21、31、41、51..91的個位各有一個。    

【用例5】10000

        答案:401。因爲只有10000的萬位含有1;千位含有1共10^3個數;同理,百位和個位。   

【用例6】21345

        答案:18821。稍複雜,算法思路是不斷降低位數,逐步的計算從高位到低位中1的數目。

2、算法思路

算法思路很簡單,每次去掉最高位做遞歸。從高位到低位計算是1的個數,再累加。
咱們還是以【用例6】來具體介紹,每次劃分爲兩個區間,如下所示。

2.1、嘗試性的思路

21345
區間一: 1~20000
區間二: 20001~21345
求解區間一,最高位(萬位)是1的數是10000~19999共10^4個;次高位(千位)是1的數是01000~01xxx和11000~11xxx共2*10^3個;第三高位(百位)是1的數是0x100~0x1xx和1x100~1x1xx共2*10^3個;同理,十位是1的數是2*10^3個,個位是1的數是2*10^3個。
求解區間二,因爲最高位確定是2了,等價於求0001~1345,即用例1345。

1345
區間一: 1~1000
區間二: 1001~1345
求解區間一,最高位(千位)是1的數是1000共1個;次高位(百位)是1的數是0100~01xx共10^2個;同理,十位是1的數共10^2個,個位是1的數共10^2個。
求解區間二,因爲最高位(千位)是1的數是1001~1345共345個;因爲最高位確定是1了,等價與求001~345,即用例345。

345
區間一: 1~300
區間二: 301~345
求解區間一,最高位(百位)是1的數共100個;次高位(十位)是1的數是010~01x和110~11x和210~21x共3*10個;同理,個位是1的數共3*10個。
求解區間二,因爲最高位確定是3了,等價於求01~45,即用例45。

45
區間一: 1~40
區間二: 41~45
求解區間一,最高位(十位)是1的數共10個;次高位(個位)是1的數是01、11、21、31共4個。
求解區間二,因爲最高位確定是4了,等價於求1~5,即用例5,所以1的個數共1個。

綜上可知,sum=(10^4+2*10^3*4)+(1+10^2*3)+345+(100+3*10*2)+(10+4)+(1)
=18000+301+345+160+14+1
=18821


2.2、整理後的思路

在上邊步驟中,如果最高位是1,那麼在求解區間2時要計算最高位是1的個數(這在區間1中也要算,其實分兩次不太合適)。所以,爲了簡化算法,統一的把它拿出來單獨計算,作爲區間零,而在區間二和三中都不再重複計算重複最高位是1的個數。
21345
區間零:10000~19999
區間一: 1~20000,排除區間零(最高位是1的情況)中的
區間二: 20001~21345
求解區間零,最高位(萬位)是1的數是10000~19999共10^4個
求解區間一,次高位(千位)是1的數是01000~01xxx和11000~11xxx共2*10^3個;第三高位(百位)是1的數是0x100~0x1xx和1x100~1x1xx共2*10^3個;同理,十位是1的數是2*10^3個,個位是1的數是2*10^3個。
求解區間二,因爲最高位確定是2了,等價於求0001~1345,即用例1345。

1345
區間零:1000~1345
區間一: 1~1000
區間二: 1001~1345
求解區間零,最高位(千位)是1的數是1000~1345共346個
求解區間一,次高位(百位)是1的數是0100~01xx共10^2個;同理,十位是1的數共10^2個,個位是1的數共10^2個。
求解區間二,因爲最高位確定是1了,等價與求001~345,即用例345。

345
區間零:100~199
區間一: 1~300
區間二: 301~345
求解區間零,最高位(百位)是1的數共100個
求解區間一,次高位(十位)是1的數是010~01x和110~11x和210~21x共3*10個;同理,個位是1的數共3*10個。
求解區間二,因爲最高位確定是3了,等價於求01~45,即用例45。

45
區間零:10~19
區間一: 1~40
區間二: 41~45
求解區間零,最高位(十位)是1的數共10個
求解區間一,次高位(個位)是1的數是01、11、21、31共4個。
求解區間二,因爲最高位確定是4了,等價於求1~5,即用例5,所以1的個數共1個。

綜上可知,sum=(10^4+2*10^3*4)+(1+10^2*3)+345+(100+3*10*2)+(10+4)+(1)
=18000+301+345+160+14+1
=18821

3、算法實現

3.1、原始的方法

這裏寫圖片描述

// ====================原始的方法(累加1到n中每個整數1出現的次數)====================
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;
}

這裏寫圖片描述

3.2、在2中介紹的方法

// ====================在2中介紹的方法(每次去掉最高位做遞歸)====================
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;
}

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章