從1到N的整數中1出現的次數
阿新 • • 發佈:2018-12-10
題目:輸入一個整數N,輸出從1到N所有整數中1的個數。例如,輸入數10,輸出為2。
解題思路一:遍歷從1到N
的所有數字,求出每個數中1的個數
,然後累加
起來,就是最終的結果。
程式碼實現:
//求出每個數中1的個數
int NmberOfI(unsigned int n)
{
int number = 0;
while (n)
{
if (n % 10 == 1)
{
number++;
}
n /= 10;
}
return number;
}
//求1到N所有整數中1的個數
int NumberOfOne(unsigned int n)
{
int number = 0;
for (size_t i = 1; i <= n; i++)
{
number += NmberOfI(i);
}
return number;
}
對於上邊的程式碼,假設每個數中有N個1,那麼它的時間複雜度為O(n*n)。因為上述程式碼對於每個數都進行了除或者模運算,效率比較低。
解題思路二:以計算1-21345中的1的個數為例。
把這段數分為1-1345
和1346-21345
兩段計算。
- 1.首先,我們先計算
10000-19999
20000
,存在10000-19999
這段,則1的個數為10000個
;另一種情況是所給的數沒有超過20000
,比如說12345,那麼在這段中1的個數則為2345+1=2346
。 - 2.第二步,我們計算
1346-21345
這段中要多少個1。因為10000-19999
的萬位已經計算過了,那麼就只剩下後邊的四位了。因為這段既存在以1位萬位的,也存在以2位萬位的,所以我們可以把1346-21345
分成兩段1346-11345
和11346-21345
。在每一段中,萬位1的個數已經確定,剩下的四位,先確定一位為1,總共四種可能,剩下的三位取值在0-9
,所以總共1的個數為2*4*10^3=8000種
- 3.最後一步,是求
1-1345這段
1-345
和346-1345
兩端計算,顯然這裡可以用遞迴解決。
程式碼實現:
int PowerBase10(unsigned int n)
{
int result = 1;
for (unsigned int i = 0; i < n; i++)
{
result *= 10;
}
return result;
}
int NmberOfI_op(const char* str)
{
assert(str);
if (*str<'0' || *str>'9' || *str == '\0')
return 0;
int first = *str - '0';
int len = strlen(str);
if (len == 1 && first == 0)
return 0;
if (len == 1 && first > 0)
return 1;
//1.以21345為例,第一段10000-19999
int numfirstdigit = 0;
//第一個數大於2,則萬位有10000個1
if (first > 1)
numfirstdigit = PowerBase10(len - 1);
//第一個數等於1,則萬位有後邊幾位的數+1(12345萬位位1有2346個)
else if (first == 1)
numfirstdigit = atoi(str + 1) + 1;
//2.1346-21345(剩下的4位,1的位置可以有四種情況,1固定好之後,其他位有0-9十種情況,既4*10^3)
//但是1346-21345應該分為兩端1346-11345、11346-21345,所以是8*10^3個
int numotherdigit = first*(len - 1)*PowerBase10(len - 2);
//3.0-1035(和上述方法一樣,採用遞迴解決)
int numrecursion = NmberOfI_op(str + 1);
//最後1的個數是前三步的和
return numfirstdigit + numotherdigit + numrecursion;
}
//將數字轉換為字串方便計算
int NumberOfOne_op(int n)
{
if (n <= 0)
return 0;
char str[50];
//將一些內容格式化輸入到字串中
sprintf(str, "%d", n);
return NmberOfI_op(str);
}