編程之美 計算1的個數 1~n中1的個數 --- 二進制中1的個數 ---總結

1.給定一個十進制整數N,求出從1到N的所有整數中出現”1”的個數。

例如:N=2,1,2出現了1個“1”

N=12,1,2,3,4,5,6,7,8,9,10,11,12。出現了5個“1”

2.給定一個十進制整數N,求其對應的二進制中“1”的個數


問題一

方法一:每個數字一位一位的計算(效率  不好)

#include <stdio.h>

//計算每個數字中“1”的個數
int count1InAInteger(int n)
{
    int count = 0;
    while (n != 0) {
        count += (n % 10 == 1) ? 1 : 0;
        n /= 10;
    }
    return count;
}

int computer1InNum(int n)
{
    int iCount = 0;
    for (int i = 1 ; i <= n ; i++)
    {
        iCount += count1InAInteger(i);
    }
    return iCount;
}


int main(int argc, const char * argv[]) {
    printf("%d\n",computer1InNum(23));
    return 0;
}

方法2:(針對每個數每一位上肯可能出現“1”的次數)

1位數的情況:

       在解法二中已經分析過,大於等於1的時候,有1個,小於1就沒有。

2位數的情況:

       N=13,個位數出現的1的次數爲2,分別爲1和11,十位數出現1的次數爲4,分別爲10,11,12,13,所以f(N) = 2+4。

       N=23,個位數出現的1的次數爲3,分別爲1,11,21,十位數出現1的次數爲10,分別爲10~19,f(N)=3+10。

       由此我們發現,個位數出現1的次數不僅和個位數有關,和十位數也有關,如果個位數大於等於1,則個位數出現1的次數爲十位數的數字加1;如果個位數爲0,個位數出現1的次數等於十位數數字。而十位數上出現1的次數也不僅和十位數相關,也和個位數相關:如果十位數字等於1,則十位數上出現1的次數爲個位數的數字加1,假如十位數大於1,則十位數上出現1的次數爲10。

       3位數的情況:

       N=123

       個位出現1的個數爲13:1,11,21,…,91,101,111,121

       十位出現1的個數爲20:10~19,110~119

       百位出現1的個數爲24:100~123

       我們可以繼續分析4位數,5位數,推導出下面一般情況: 

       假設N,我們要計算百位上出現1的次數,將由三部分決定:百位上的數字,百位以上的數字,百位一下的數字。

       如果百位上的數字爲0,則百位上出現1的次數僅由更高位決定,比如12013,百位出現1的情況爲100~199,1100~1199,2100~2199,…,11100~11199,共1200個。等於更高位數字乘以當前位數,即12 * 100。

       如果百位上的數字大於1,則百位上出現1的次數僅由更高位決定,比如12213,百位出現1的情況爲100~199,1100~1199,2100~2199,…,11100~11199,12100~12199共1300個。等於更高位數字加1乘以當前位數,即(12 + 1)*100。

        如果百位上的數字爲1,則百位上出現1的次數不僅受更高位影響,還受低位影響。例如12113,受高位影響出現1的情況:100~199,1100~1199,2100~2199,…,11100~11199,共1200個,但它還受低位影響,出現1的情況是12100~12113,共114個,等於低位數字113+1。

       綜合以上分析,寫出如下代碼:

#include <stdio.h>
int count1InAInteger(int n)
{
    int count = 0;
    int i = 1;
    int current = 0,after = 0,before = 0;
    while((n / i) != 0)
    {
        current = (n / i) % 10;
        before = n / (i * 10);
        after = n - (n / i) * i;
        if (current > 1)
            count = count + (before + 1) * i;
        else if (current == 0)
            count = count + before * i;
        else if(current == 1)
            count = count + before * i + after + 1;
        i = i * 10;
    }
    return count;
}
int main(int argc, const char * argv[]) {
    printf("%d\n",count1InAInteger(23));
    return 0;
}

問題二

方法一:(位運算)

int count(unsigned int n)
{
    int sum = 0;
    while(n)
    {
        sum += n & 1;
        n >>= 1;
    }
    return sum;
}
int main(int argc, const char * argv[]) {
    printf("%d\n",count(10));
    return 0;
}

方法二:(除法)

int count(unsigned int n)
{
    int sum = 0;
    while(n)
    {
        if(n % 2 == 1)
            sum++;
        n /= 2;
    }
    return sum;
}

int main(int argc, const char * argv[]) {
    printf("%d\n",count(10));
    return 0;
}

3優化

int count(unsigned int n)
{
    int sum = 0;
    while(n)
    {
        n &= (n-1);
        sum++;
    }
    return sum;
}

int main(int argc, const char * argv[]) {
    printf("%d\n",count(10));
    return 0;
}








發佈了203 篇原創文章 · 獲贊 44 · 訪問量 49萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章