問題來源
https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/
暴力法
對於暴力的方法,只需要從1開始,對1 -> n
中的所有數進行枚舉,然後依次判斷該數的每一位(個位,十位,百位 .... ....)
是否爲1
- 如果該位是1,計數器
ans++
- 如果該位不是1,繼續判斷下一位
int countDigitOne(int n) {
int ans = 0;
for(int i = 1; i <= n; i++)
{
int num = i;
while(num)
{
if(num % 10 == 1)
ans++;
num /= 10;
}
}
return ans;
}
當數據量特別大的時候,最後的結果是會超時的
優化後的寫法
既然暴力的方法不可以,那麼我們是否可以根據當前位(個位,十位,百位 .... ....)
的數字,然後確定出以該位爲中心,一共有多少個1的情況。
對於這種做法,我們可以把一個數字(n n-1 … … 1)分成三部分,設當前位爲第 x 位:
- 高位,n - > x + 1 位,也就是 n n-1 … … x + 1
- 當前位,第 x 位,也就是 x
- 低位,x-1 x-2 … … 1,也就是 x - 1 x - 2 1
對於當前位的數值,有這麼幾種情況
- 當前位的數值爲 0 ,也就是 x = 0
-
當前位的數值爲1,也就是 x = 1
-
當前位的數值大於1,也就是 x > 1
int countDigitOne(int n) {
int ans = 0;
long i = 1; //i表示哪一位的數字,i = 1表示個位,i = 10 表示十位
while(n / i != 0)
{
//當前位的高位數字
long high = n / (10 * i);
//當前位的數字
long cur = (n / i) % 10;
//當前位的低位數字
long low = n - (n / i) * i;
if(cur == 0)
ans += high * i;
else if(cur == 1)
ans += high * i + low + 1;
else
ans += (high + 1) * i;
i *= 10;
}
return ans;
}
算法的時間複雜度爲O(log n)
,也就是 n 有多少位,就循環多少次