程式設計之美系列之三 計算1的個數

2021-07-06 01:11:01 字數 3264 閱讀 2136

給定乙個十進位制整數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」。

問題求解:

解法一:

最直接的方法就是從1開始遍歷到n,將其中每乙個數中含有「1」的個數加起來,就得到了問題的解。

**如下:

1

public

long countone3(

long n)

2

14 }

15return count;

16 }

此方法簡單,容易理解,但它的問題是效率,時間複雜度為o(n * lgn),n比較大的時候,需要耗費很長的時間。

解法二:

我們重新分析下這個問題,對於任意乙個個位數n,只要n>=1,它就包含乙個「1」;n<1,即n=0時,則包含的「1」的個數為0。於是我們考慮用分治的思想將任意乙個n位數不斷縮小規模分解成許多個個位數,這樣求解就很方便。

但是,我們該如何降低規模?仔細分析,我們會發現,任意乙個n位數中「1」的個位可以分解為兩個n-1位數中「1」的個數的和加上乙個與最高位數相關的常數c。例如,f(12) = f(10 - 1) + f(12 - 10) + 3,其中3是表示最高位為1的數字個數,這裡就是10,11,12;f(132)=f(100 -1) + f(132 - 100) + 33,33代表最高位為1的數字的個數,這裡就是100~132;f(232) = 2*f(100 - 1) + f(32) + 100,因為232大於199,所以它包括了所有最高位為1的數字即100~199,共100個。

綜上,我們分析得出,最後加的常數c只跟最高位n1是否為1有關,當最高位為1時,常數c為原數字n去掉最高位後剩下的數字+1,當最高位為1時,常數c為10bit,其中bit為n的位數-1,如n=12時,bit=1,n=232時,bit=2。

於是,我們可以列出遞迴方程如下:

if(n1 == 1)

f(n) = f(10bit-1) + f(n - 10bit)  + n - 10bit+ 1;

else

f(n) = n1*f(10bit-1) + f(n – n1*10bit) + 10bit;

遞迴的出口條件為:

if(1else if (n == 0) return 0;

基於此,編寫如下**:

1

public

long countone(

long n)

2

1718int weight = (

int)math.pow(

10, bit);

//代表最高位的權重,即最高位乙個1代表的大小

19if (highest ==

1)20

25else

26

31 }

32return count;

33 }

此演算法的優點是不用遍歷1~n就可以得到f(n)。經過我測試,此演算法的運算速度比解法一快了許多許多,數字在1010內時,演算法都可以在毫秒級內結束,而解法一在計算109時,時間超過了5分鐘。但此演算法有乙個顯著的缺點就是當數字超過1010時會導致堆疊溢位,無法計算。

還有就是,我嘗試了許久也沒有計算出此演算法的時間複雜度到底是多少,似乎是o(lg2n),我並不確定,希望知道的高手能給予解答。

解法三:

解法二告訴我們1~ n中「1」的個數跟最高位有關,那我們換個角度思考,給定乙個n,我們分析1~n中的數在每一位上出現1的次數的和,看看每一位上「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。

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

1

public

long countone2(

long n)

2

21return count;

2223 }

此演算法的時間複雜度僅為o(lgn),且沒有遞迴儲存現場的消耗和堆疊溢位的問題。

不知道各位看官是否還有更高效的演算法拿出來分享呢?

計算1的個數 程式設計之美

給定乙個十進位制正數n,寫下從1開始到nde所有正整數,然後數一數其中出現所有 1 的個數。n 2 只有乙個1 n 12 只有五個1,1 10,11,12。分析 一,從1開始遍歷到n,將中每乙個書中含有1的個數加起來。方法如下 static int countoneinint int n retur...

《程式設計之美》統計1的個數

今天看了 入門經典 上的一道題,打算來存個統計位數的模板,一般都是用數字dp來做,但統計單個數字可以特殊處理。題目 對1 n統計1的個數。題目簡單,分析可以看 舉個例子 對於乙個數abcde,取百位上的c來計算,假若c是 1 那麼百位上1的個數是由他的高位和低位來決定的。等於ab 100 cde 1...

程式設計之美 計算1 N中含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的次數為...