[劍指offer] 整數中1的出現次數 java

以下分析來自牛客網討論區,分析的很清晰。

---------------------------------------------------------------------------------------------------------------------------
像類似這樣的問題,我們可以通過歸納總結來獲取相關的東西。

首先可以先分類:

我們知道在個位數上,1會每隔10出現一次,例如1、11、21等等,我們發現以10爲一個階梯的話,每一個完整的階梯裏面都有一個1,例如數字22,按照10爲間隔來分三個階梯,在完整階梯0-9,10-19之中都有一個1,但是19之後有一個不完整的階梯,我們需要去判斷這個階梯中會不會出現1,易推斷知,如果最後這個露出來的部分小於1,則不可能出現1(這個歸納換做其它數字也成立)。

我們可以歸納個位上1出現的個數爲:n/10 * 1+(n%10!=0 ? 1 : 0)。

現在說十位數,十位數上出現1的情況應該是10-19,依然沿用分析個位數時候的階梯理論,我們知道10-19這組數,每隔100出現一次,這次我們的階梯是100,例如數字317,分析有階梯0-99,100-199,200-299三段完整階梯,每一段階梯裏面都會出現10次1(從10-19),最後分析露出來的那段不完整的階梯。我們考慮如果露出來的數大於19,那麼直接算10個1就行了,因爲10-19肯定會出現;如果小於10,那麼肯定不會出現十位數的1;如果在10-19之間的,我們計算結果應該是k - 10 + 1。例如我們分析300-317,17個數字,1出現的個數應該是17-10+1=8個。那麼現在可以歸納:十位上1出現的個數爲:

·設k= n % 100,即爲不完整階梯段的數字

·歸納式爲:(n / 100) * 10 + (if(k > 19) 10 else if(k < 10) 0 else k - 10 + 1)

現在說百位1,我們知道在百位,100-199都會出現百位1,一共出現100次,階梯間隔爲1000,100-199這組數,每隔1000就會出現一次。這次假設我們的數爲2139。跟上述思想一致,先算階梯數 * 完整階梯中1在百位出現的個數,即n/1000 * 100得到前兩個階梯中1的個數,那麼再算漏出來的部分139,沿用上述思想,不完整階梯數k<100則得到0個百位1,k>199,得到100個百位1,100<=k<=199則得到k - 100 + 1個百位1。那麼繼續歸納:百位上出現1的個數:

·設k = n % 1000

·歸納式爲:(n / 1000) * 100 + (if(k >199) 10 else if(k < 100) 0 else k - 100 + 1)

後面的依次類推....

那麼我們把個位數上算1的個數的式子也納入歸納式中

·k = n % 10

·個位數上1的個數爲:n / 10 * 1 + (if(k > 1) 1 else if(k < 1) 0 else k - 1 + 1)

完美!歸納式看起來已經很規整了。

來一個更抽象的歸納,設i爲計算1所在的位數,i=1表示計算個位數的1的個數,10表示計算十位數的1的個數等等。

·k = n % (i * 10)

·count(i) = (n / (i * 10)) * i + (if(k > i * 2 - 1) i else if(k < i) 0 else k - i + 1)

好了,這樣從10到10的n次方的歸納就完成了。

sum1 = sum(count(i)),i = Math.pow(10, j), 0<=j<=log10(n)

 

但是有一個地方值得我們注意的,就是代碼的簡潔性來看,有多個ifelse不太好,能不能進一步簡化呢?

我們可以把後半段簡化成這樣,我們不去計算i * 2 - 1了,我們只需保證k - i + 1在[0, i]區間內就行了,最後後半段可以攜程這樣

min(max((n mod (i*10))−i+1,0),i)

 

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        if(n<=0) return 0;
        int count=0;
        for(int i=1;i<=n;i=i*10){
            int temp=i*10;
            count+=(n/temp)*i+Math.min(Math.max(n%temp-i+1,0),i);
        }
        return count;    
    }
}

我自己在看代碼 Math.min(Math.max(n%temp-i+1,0),i)時,不太懂。後來發現就是爲了減少if判斷次數,爲什麼是這個式子呢。

Math.max(n%temp-i+1,0)這句就是算①②的最大值,然後Math.min(Math.max(n%temp-i+1,0),i)將①②中的最大值與③比較,他們中最小的就是我們要的結果。這樣就避免使用很多判斷語句了。

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