劍指offer面試題[32]:從1到n整數中1出現的次數
題目描述
求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?為此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數。
(題目意思是:輸入一個整數n,求從1到n這n個整數的十進位制表示中1出現的次數)
思路分析:
思路一:(不考慮時間效率的解法,靠它想拿offer有點難)
從1到n遍歷求每個數字中1的個數,然後累加,每個數用求餘運算(%),若餘數為1,則說明該位為1。大多數人應該可以求解。
思路二:(從數字規律著手明顯提高效率的做法,能讓面試官耳目一新)
如果希望不用計算每個數字的1的個數,那就只能去尋找1在數字中出現的規律了。為了找到規律,我們不妨用一個稍微大一點的數字如21345作為例子來分析。我們把從1到21345分為兩段,一段是從1到1345,另一段是從1346到21345。
我們先看從1346到21345中1出現的次數。1的出現分兩種情況。首先分析1出現在最高位(本例中是萬位)的情況。從1346到21345的數字中,1出現在10000到19999這10000個數字的萬位中,一共出現了10000(10^4)個。
值得注意的是,並不是對所有5位數而言在萬位出現的次數都是10000個。對於萬位是1的數字比如輸入12345,1只出現在10000到12345的萬位,出現的次數不是10^4次,而是2346次,也就是除去最高數字位之後剩下的數字再加上1(即2345+1=2346)。
接下來分析1出現在除最高位之外的其他四位數中的情況。本例中1346到21345這2000個數字後四位中1出現的次數是8000次。由於最高位是2,我們可以再把1346到21345分為兩段,1346到11345和11346到213456.每一段剩下的4位數字中,選擇其中一位是1,其餘三位可以在0~9這10個數字中任選,因此根據排序組合的原則,總共出現的次數是2*4*10^3=8000次。
至於1到1345中1出現的次數,我們就可以根據遞迴求得了。這也就是為什麼把1~21345分為1~1345和1346~21345兩段的原因。因為把21345的最高位去掉就變為1345,便於我們採用遞迴的思路。
參考程式碼如下:
#include <iostream>
#include <stdio.h>
using namespace std;
class Solution {
public:
/*方法1:
int NumberOf1Between1AndN_Solution(int n)
{
if(n<=0)
return 0;
int number=0;
for(int i=1;i<=n;i++)
{
int temp=i;
while(temp) //注意這裡一定是用一箇中間臨時變數來存取每次讀入的數字i
{
int s=temp%10;
if(s==1)
number++;
temp=temp/10;
}
}
return number;
}
*/
//方法2:
int NumberOf1Between1AndN_Solution(int n)
{
if (n <= 0)
return 0;
char strN[50];
sprintf_s(strN, "%d", n); //為了程式設計方便,將數字轉換為字串
return NumberOf1(strN);
}
int powerBase10(int n)
{
int result = 1;
for (int i = 0; i<n; i++)
result *= 10;
return result;
}
int NumberOf1(char *strN)
{
if (strN == NULL || *strN<'0' || *strN>'9' || *strN == '\0')
return 0;
int first = *strN - '0';
int length = static_cast<int>(strlen(strN));
if (length == 1 && first == 0)
return 0;
if (length == 1 && first>0)
return 1;
//假設strN是"21345"
//numFirstDigit是數字10000~19999的第一位中的數目
int numFirstDigits = 0;
if (first>1)
numFirstDigits = powerBase10(length - 1);
else if (first == 1)
numFirstDigits = atoi(strN + 1) + 1; //atoi是把字串轉換成整型數的一個函式
//numOtherDigits是1346~21345除了第一位之外的數位中的數目
int numOtherDigits = first*(length - 1)*powerBase10(length - 2);
int numRecursive = NumberOf1(strN + 1);
return numFirstDigits + numOtherDigits + numRecursive;
}
};
int main()
{
Solution s;
int n;
cin >> n;
int CountOf1=s.NumberOf1Between1AndN_Solution(n);
cout << CountOf1 << endl;
system("pause");
}