【Algorithm】《劍指offer》面試題32----從1到n整數中1出現的次數
原貼地址:https://www.cnblogs.com/xuanxufeng/p/6854105.html
題目描述
求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?為此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。
解法:分析數字規律,時間複雜度O(logn).
這是我寫這篇文章的初衷。《劍指offer》洋洋灑灑寫了幾十行程式碼,然而在leetcode上大神卻只用了5行!當天晚上智障,腦子全是漿糊,竟然沒有看懂什麼意思=。=,我一度懷疑智商受到了碾壓。然而在今天睡眠比較充足,頭腦比較清醒的情況下終於理順了思路~
其實這道題目很多地方都有講,包括《程式設計之美》,但是也有20行左右的程式碼,沒耐心了。其它的一些帖子講的亂七八糟,這對於我這種愛簡潔,愛乾淨,還有嚴重強迫症的人是不能忍的,下面強迫症患者要開始裝逼了。。。
先上程式碼:
package test;
public class Question_32 {
public static int countDigitOne(int n) {
int ones = 0;
for (long m = 1; m <= n; m *= 10)
ones += (n/m + 8) / 10 * m + (n/m % 10 == 1 ? n%m + 1 : 0);
return ones;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(countDigitOne(12));
}
}
裝逼模式開啟:
我們從一個5位的數字講起,先考慮其百位為1的情況。分3種情況討論:
- 百位數字>=2 example: 31256 當其百位為>=時,有以下這些情況滿足(為方便起見,計312為a,56為b):
100 ~ 199
1100 ~ 1199
.....
31100 ~ 31199
餘下的都不滿足!
因此,百位>=2的5位數字,其百位為1的情況有(a/10+1)*100個數字
(a/10+1)=>對應於 0 ~ 31,且每一個數字,對應範圍是100個數(末尾0-99)
- 百位數字 ==1 example: 31156 當其百位為1時,有以下這些情況滿足:
100 ~ 199
1100 ~ 1199
......
30100 ~ 30199
31100 ~ 31156
因此,百位為1的5位數字,共有(a/10)*100+(b+1)
- 百位數字 ==0 example: 31056 當其百位為0時,有以下這些情況滿足:
100 ~ 199
1100 ~ 1199
30100 ~ 30199
其餘都不滿足
因此,百位數為0的5位數字,共有(a/10)*100個數字滿足要求
我們可以進一步統一以下表達方式:
即當百位>=2或=0時,有[(a+8)/10]*100,當百位=1時,有[(a+8)/10]*100+(b+1)。
用程式碼表示就是: [(a+8)/10]*100+(a%10==1)?(b+1):0;
為什麼要加8呢?因為只有大於2的時候才會產生進位等價於(a/10+1),當等於0和1時就等價於(a/10)。另外,等於1時要單獨加上(b+1),這裡我們用a對10取餘是否等於1的方式判斷該百位是否為1。
Question:有缺陷或邏輯錯誤嗎?
有人可能會有疑惑,比如11100,這個數在考慮百位為1的時候算作了一次,在考慮千位的時候也算了一次,在考慮萬位為1的時候又算了一次,一共計了3次,這不是明顯重複嗎?
我的回答是,不重複!
分析:題目中要我們統計出現的1的個數,那麼我們可以看到11100一共是3個1,如果剔除了重複的情況只考慮一次才會是問題。換言之,在計算從1到n整數中1的出現次數時,我們把10位出現1的情況個數加上百位出現1的情況個數一直加到最高位是1的情況的個數,這裡面一個數可能被統計過多次;11100百位出現1,千位和萬位都為1,那麼被重複統計了3次
轉載者的BB:計算的是每一位上出現1的個數,不是出現1的數的個數
程式碼分析:
public static int countDigitOne(int n) {
int ones = 0;
for (long m = 1; m <= n; m *= 10)
ones += (n/m + 8) / 10 * m + (n/m % 10 == 1 ? n%m + 1 : 0);
return ones;
}
for (long m = 1; m <= n; m *= 10) 在這裡的作用是,從個位開始考慮,再到十位,百位,千位,一直到超出這個數!為什麼m要用long型呢?因為n可能沒有超過整型的表達範圍(int剛好可以表示n),而10*m恰恰有可能剛剛超過!ones += (n/m + 8) / 10 * m + (n/m % 10 == 1 ? n%m + 1 : 0); 這裡ones用於表示1的個數,當m=100時,n/m其實代表的是a,而n%m代表的是b,此時考慮的是百位為1的情況;當m=1000,自然考慮的就是千位等於1的情況了~ 至於為什麼加8,那個三目運算子是幹嘛子用的上面都已經講過了。
最後,總結一下。這道題網上答案太多了,但是我覺得只有這種方法最讓人眼前一亮。抖機靈的不少,比如用java字串處理的,自以為很厲害,其實根本沒含金量(時間複雜度O(nlogn)啊!)關鍵是這還有贊同的,不知道演算法分析是怎麼學的。《劍指offer》和《程式設計之美》的答案可能曾經是最佳,但是現在被更好的方法替換了,而作者並不知情。一本好書看3遍,勝過3本好書看一遍!相信一個月後,我對這道題的印象可能就沒有多少了,及時整理,利人利己,溫故而知新~
相關文章
- 劍指offer面試題12 列印1到最大的n位數面試題
- 劍指offer面試題11 數值的整數次方面試題
- 劍指 Offer 列印從1到最大n位數c++C++
- GitHub#algorithm#:《劍指offer》 的50道面試題GithubGo面試題
- 【劍指offer】字串轉整數字串
- 【劍指offer】陣列中只出現一次的數字(1)陣列
- 劍指Offer-17-列印從1到最大的n位數-Java程式碼實現(兩種思路)Java
- 劍指 Offer 56 - I. 陣列中數字出現的次數陣列
- 【劍指offer】數字在排序陣列中出現的次數排序陣列
- 劍指offer 陣列中只出現一次的數字陣列
- 《劍指offer》:[38]數字在排序陣列中出現的次數排序陣列
- 《劍指offer》:[49]把字串轉化成整數字串
- 【劍指offer】二進位制中1的個數
- 劍指offer面試題14 調整陣列順序使奇數位於偶數前面面試題陣列
- 《劍指offer》:[40]陣列中只出現一次的數字陣列
- 【劍指offer】陣列中只出現一次的數字(2)陣列
- 劍指Offer-39-數字在排序陣列中出現的次數排序陣列
- 劍指OFFER-數字在升序陣列中出現的次數(Java)陣列Java
- 【leetcode】劍指 Offer 16. 數值的整數次方LeetCode
- [劍指offer題解][Java]陣列中出現次數超過一半的數字Java陣列
- 劍指offer刷題之路--1.陣列中重複的數字陣列
- 【劍指Offer學習】【面試題14 :調整陣列順序使奇數位於偶數前面】面試題陣列
- 劍指 Offer 15. 二進位制中1的個數
- 劍指offer(Java版)--將字串轉換為整數Java字串
- 劍指offer——把字串轉換成整數C++字串C++
- 劍指offer面試題15 連結串列中倒數第K個結點面試題
- 劍指Offer 表示數值的字串字串
- 《劍指offer》:[64]資料流中的中位數
- 劍指Offer面試題5(Java版):從尾到頭列印連結串列面試題Java
- [劍指Offer]面試題35:第一個只出現一次的字元面試題字元
- 劍指offer面試題(41-50)——java實現面試題Java
- 從面試題中學演算法(2)---求陣列中唯一n個出現1次的數字(n=1,2,3)面試題演算法陣列
- 劍指Offer--陣列中重複的數字陣列
- 《劍指offer》:[51]陣列中的重複數字陣列
- 《劍指offer》:[54]表示數值的字串字串
- 【劍指offer中等部分4】二進位制中1的個數(java)Java
- 【劍指Offer】調整陣列順序使奇數位於偶數前面陣列
- 力扣 - 劍指 Offer 67. 把字串轉換成整數力扣字串