【劍指Offer】整數中1出現的次數(從1到n整數中1出現的次數)

【題目】

求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。

【思路】

兩種方法!分別是暴力解決和使用數學技巧。

【代碼】

1. 暴力解決

從1到n遍歷,每次通過對10求餘數判斷整數的個位數字是不是1,大於10的除以10之後再循環判斷。我們對每個數字都要做除法和求餘運算以求出該數字中1出現的次數。如果輸入數字n,n有O(log10n+1)O(log_{10}n+1)位,我們需要判斷每一位是不是1,那麼時間複雜度爲O(nlog10n)O(n*log_{10}n)

class Solution {
public:
	int NumberOf1Between1AndN_Solution(int n){
		if (n <= 0) {
			return 0;
		}
		int result = 0;
		for (int i = 1; i <= n; i++) {
			int tmp = i;
			while (tmp != 0) {
				if (tmp % 10 == 1) {
					result++;
				}
				tmp /= 10;
			}
		}
		return result;
	}
};

2. 數學技巧

從網上可以看到大量用編程之美給出的規律解題的思路,看了挺多篇文章,這裏說說自己的理解。

這裏以 736 爲例進行分析:

1)個位

我們把736分成兩個部分,一部分是個位左邊的73,另一部分是個位右邊的(因爲沒有,這裏就不用考慮了)。個位可以從0~9變化10次,此記爲一輪,每一輪出現一次數字1。然後,會出現74輪(第一部分73從0變化到73)。因此可以得出個位出現1的次數爲74。

三種情況:

  • 當個位大於1,即上面的情況。可以得出個位出現1的次數爲74。

  • 當個位等於1,和上面的情況相同。可以得出個位出現1的次數爲74。

  • 當個位等於0時,即730。第74輪個位到0就結束了,所以個位沒有出現1。因此可以得出個位出現1的次數爲73。

2)百位

我們繼續把736分成兩個部分,一部分是個位左邊的7,另一部分是個位右邊的6。百位可以從09變化10次,個位同樣是可以從09變化10次,個位變化一輪百位變化一次,百位變化一輪更高位才變化一次。另外百位變化一輪,1會出現10次,這是因爲在這一輪中百位爲1時,個位可以從0~9變化10次。 然後,百位會出現8輪(第一部分7從0變化到7)。因此可以得出百位出現1的次數爲8*10=80次。

三種情況:

  • 當百位大於1時,即上面的情況。可以得出百位出現1的次數爲80。
  • 當百位等於1時,即716。那麼第8輪中百位出現1的次數和個位有關,即出現了7次(個位從0~6變化7次)。因此可以得出百位出現1的次數爲7*10+7=77。
  • 當百位等於0時,即706。那麼第8輪中百位到0就結束了,不會出現1。因此可以得出百位出現1的次數爲7*10=70。

3)更高位

更高位的處理情況和百位的情況類似。


下面貼上AC代碼的兩種寫法,時間複雜度都是O(log10n)O(log_{10}n)

寫法一:

class Solution {
public:
	int NumberOf1Between1AndN_Solution(int n){
		if (n <= 0) {
			return 0;
		}
		int result = 0, tmp = n, base = 1;
		while (tmp > 0) {
			int a = tmp % 10;  // 本位的數
			tmp = tmp / 10;  // 高位(本位左邊的數)

			result += tmp*base;  // a==0的情況
			if (a == 1) {
				result += n%base + 1;
			}
			else if (a > 1) {
				result += base;
			}
			base *= 10;
		}
		return result;
	}
};

寫法二(簡潔優美):

class Solution {
public:
	int NumberOf1Between1AndN_Solution(int n){
		// 統計次數
		int result = 0;
		for (int i = 1; i <= n; i *= 10) {
			// 計算高位和低位
			//之所以補8,是因爲當百位爲0,則a/10==(a+8)/10,
			//當百位>=2,補8會產生進位位,效果等同於(a/10+1)
			int a = n / i, b = n % i;
			result += (a + 8) / 10 * i + ((a % 10 == 1) ? (b + 1) : 0);
		}
		return result;
	}
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章