統計在從1到n的正整數中1出現的次數

統計在從1到n的正整數中1出現的次數

1、最直觀的想法,求1到n中每個整數中1出現的次數,然後相加即可。而求每個十進制整數中1出現的次數,我們先判斷這個數的個位數是否是1,如果這個數大於10,除以10之後再判斷個位數是否爲1,循環直至求出該整數包含1的個數。

代碼如下:

#include "stdafx.h"
#include "stdlib.h"
#include <iostream>
#include <string>
#include <stack>
#include <math.h>
using namespace std;

//某個整數中1出現的次數
int NumberOf1(unsigned int n)
{
	int number = 0;	
	while(n)
	{	
		if(n % 10 == 1)		
			number ++;	
		n = n / 10;	
	}

	return number;
}

//求1到n中所有整數中1出現的總次數
int NumberOf1Between1AndN_Solution1(unsigned int n)
{
	int number = 0;	
	for(unsigned int i = 1; i <= n; ++ i)		
		number += NumberOf1(i);	

	return number;
}

void main()
{
	cout<<NumberOf1Between1AndN_Solution1(21345)<<endl; //18821
}

2、上面的思路有一個非常明顯的缺點就是每個數字都要計算1在該數字中出現的次數,因此時間複雜度是O(n)。當輸入的n非常大的時候,需要大量的計算,運算效率很低。我們試着找出一些規律,來避免不必要的計算。

        我們用一個稍微大一點的數字21345作爲例子來分析。我們把從1到21345的所有數字分成兩段即1-1345和1346-21345(分段好處在於便於進行遞歸運算,因爲1345位21345去掉最高位的結果)。

        先來看1346-21345中1出現的次數。1的出現分爲兩種情況:一種情況是1出現在最高位(萬位)。從1到21345的數字中,1出現在10000-19999這10000個數字的萬位中,一共出現了10000(10^4)次;另外一種情況是1出現在除了最高位之外的其他位中。例子中1346-21345,這20000個數字中後面四位中1出現的次數是2000次(2*10^3,其中2的第一位的數值,10^3是因爲數字的後四位數字其中一位爲1,其餘的三位數字可以在0到9這10個數字任意選擇,由排列組合可以得出總次數是2*10^3)。

        至於從1到1345的所有數字中1出現的次數,我們就可以用遞歸地求得了。這也是我們爲什麼要把1-21345分爲1-1345和1346-21345兩段的原因。因爲把21345的最高位去掉就得到1345,便於我們採用遞歸的思路。

        分析到這裏還有一種特殊情況需要注意:前面我們舉例子是最高位是一個比1大的數字,此時最高位1出現的次數10^4(對五位數而言)。但如果最高位是1呢?比如輸入12345,從10000到12345這些數字中,1在萬位出現的次數就不是10^4次,而是2346次了,也就是除去最高位數字之後剩下的數字再加上1。

基於前面的分析,我們可以寫出以下的代碼。在參考代碼中,爲了編程方便,我把數字轉換成字符串了。

#include "stdafx.h"
#include "stdlib.h"
#include <iostream>
#include <string>
#include <stack>
#include <math.h>
using namespace std;

//1到n字符串中包含1的總個數
int CharNumberOf1(const char *strN)
{
	int numFirstDigit=0,numOtherDigit=0,numRecursive=0;
	int len=strlen(strN);
	int firstDigit=*strN-'0'; 
	if(len==1&&firstDigit==0)
		return 0;
	if(len==1&&firstDigit>0)
		return 1;
	//首位爲1的個數
	if(firstDigit==1)
		numFirstDigit=atoi(strN+1)+1;
	else
		numFirstDigit=pow(10,len-1);
	//其他位爲1的個數
	numOtherDigit=firstDigit*(len-1)*pow(10,len-2);
	//遞歸的位爲1的個數
	numRecursive=CharNumberOf1(strN+1);

	return numFirstDigit+numOtherDigit+numRecursive;
}

//1到n整數中包含1的總個數
int NumberOf1(int n)
{
	char strN[50];
	sprintf(strN,"%d",n);

	return CharNumberOf1(strN);
}

void main()
{
	cout<<NumberOf1(21345)<<endl; //18821
}

 

發佈了96 篇原創文章 · 獲贊 19 · 訪問量 57萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章