求1~n整數中1出現的次數(《劍指offer》面試題43)


題意:

  給定一個整數n,求1~n這n個整數中十進制表示中1出現的次數。

思路:

  方法1:最直觀的是,對於1~n中的每個整數,分別判斷n中的1的個數,具體見《劍指offer》。這種方法的時間復雜度為O(N*logN),當N比較大的時候,一般會超時。

  方法2:這種類別的題目,如果直觀求解不行的話,那么通常是進行找規律,轉化成一個數學問題。這道題目在《編程之美》上有着比較詳細的描述,下面就結合一個實例進行具體的分析:

在分析之前,首先需要知道一個規律:

  • 從 1 至 10,在它們的個位數中,數字1出現了 1 次。
  • 從 1 至 100,在它們的十位數中,數字1出現了 10 次。
  • 從 1 至 1000,在它們的百位數中,數字1出現了 100 次。

依此類推,從 1 至 10i,在它們右數第二位中,數字1出現了10 ^ (i - 1)次。

對於 n = 2134,要找到從1 ~ 2134這2134個數字中所有1的個數。我們可以對2134進行逐位分析:

(1)在個位上,從1~2130,包含213個10,因此數字1出現了213次,剩下的數字2131、2132、2133、2134中個位數上只有2131包含樹脂字1,剩下的都不包含。所以個位數上的數字1的總數為213 + 1 = 214。

(2)在十位上,從1 ~ 2100,包含了21個100,因此數字1出現了21 * 10 = 210次,剩下的數字從2101 ~ 2134,只有2110 ~ 2119這10個數字中十位的數字為1,所以十位上的數字1的總數為210 + 10 = 220。

(3)在百位上,從1 ~ 2000,包含了2個1000,因此數字1出現了2 * 100 = 200次,剩下的數字從2001 ~ 2134,只有2100 ~ 2134這35個數字中的百位的數字為1,所以百位數上數字1的總數為200 + 35= 235。

(4)在千位上,包含了0個10000,因此數字1出現了0 * 1000 = 0次,剩下的數字中只有1000 ~ 1999這1000個數字中的千位的數字為1,所以千位上的數字1的總數為1000。

因此從1 ~ 2134這n個數字中,數字出現的總的次數為 214 + 220 + 235 +1000 = 1669。

總結一下以上的步驟,可以得到這么一個規律:

對於數字n,計算它的第i(i從1開始,從右邊開始計數)位數上包含的數字1的個數:

假設第i位上的數字為x的話,則

1.如果x > 1的話,則第i位數上包含的1的數目為:(高位數字 + 1)* 10 ^ (i-1)  (其中高位數字是從i+1位一直到最高位數構成的數字)

2.如果x < 1的話,則第i位數上包含的1的數目為:(高位數字 )* 10 ^ (i-1)

3.如果x == 1的話,則第i位數上包含1的數目為:(高位數字) * 10 ^ (i-1) +(低位數字+1)   (其中低位數字時從第i - 1位數一直到第1位數構成的數字)

所以,代碼如下:

int NumberOfDigitOne(int n) {
    if( n < 0)
        return 0;
    int i = 1;
    int high = n;
    int cnt = 0;
    while(high != 0)
    {
        high = n / pow(10 ,i);//high表示當前位的高位
        int temp = n / pow(10, i - 1);
        int cur = temp % 10;//cur表示第i位上的值,從1開始計算
        int low = n  - temp * pow(10, i - 1);//low表示當前位的低位
        if(cur < 1)
        {
            cnt += high * pow(10, i - 1);
        }
        else if(cur > 1)
        {
            cnt += (high + 1) * pow(10 ,i - 1);

        }
        else
        {

            cnt += high * pow(10, i - 1);
            cnt += (low + 1);

        }
        i++;
    }
    return cnt;
}

該算法的時間復雜度為O(logN)。

===================================================================================================================================
上述算法是計算數字1的個數,對於其他的數字k(1~9中的任意數字),這個規律同樣也是適用的,分析的過程也是一樣的,只不過將上述的1改為k即可。下面代碼是計算1 ~n中任意數字出現的次數:

int NumberOfDigitX(int n, int x) {
    if(x > 9 || x < 0 || n < 0)
        return 0;
    int i = 1;
    int high = n;
    int cnt = 0;
    while(high != 0)
    {
        high = n / pow(10 ,i);//high表示當前位的高位
        int temp = n / pow(10, i - 1);
        int cur = temp % 10;//cur表示第i位上的值,從1開始計算
        int low = n  - temp * pow(10, i - 1);//low表示當前位的低位
        if(cur < x)
        {
            cnt += high * pow(10, i - 1);
        }
        else if(cur > x)
        {
            cnt += (high + 1) * pow(10 ,i - 1);

        }
        else
        {

            cnt += high * pow(10, i - 1);
            cnt += (low + 1);

        }
        i++;
    }
    return cnt;
}

 

參考:http://www.cnblogs.com/cyjb/p/digitOccurrenceInRegion.html

附:求1~n中數字0的個數

int NumberOfDigitZero(int n) {
    if(n < 0)
        return 0;
    int i = 1;
    int high = n;
    int cnt = 0;
    while(high != 0)
    {
        high = n / pow(10 ,i);//high表示當前位的高位
        int temp = n / pow(10, i - 1);
        int cur = temp % 10;//cur表示第i位上的值,從1開始計算
        int low = n  - temp * pow(10, i - 1);//low表示當前位的低位
        if(cur < 0)//實際上這步不會執行
        {
            cnt += (high - 1) * pow(10, i - 1);
        }
        else if(cur > 0)
        {
            cnt += (high) * pow(10 ,i - 1);

        }
        else
        {

            cnt += (high - 1)* pow(10, i - 1);
            cnt += (low + 1);

        }
        i++;
    }
    return cnt;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM