【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 列印從1到最大n位數c++C++
- 【劍指 Offer 】17. 列印從1到最大的n位數
- 劍指offer | 17. 列印從1到最大的n位數
- JZ-031-從 1 到 n 整數中 1 出現的次數
- GitHub#algorithm#:《劍指offer》 的50道面試題GithubGo面試題
- 劍指Offer-17-列印從1到最大的n位數-Java程式碼實現(兩種思路)Java
- 劍指offer:輸入n個整數,找出其中最小的K個數。
- 劍指 Offer 56 - I. 陣列中數字出現的次數陣列
- 【leetcode】劍指 Offer 16. 數值的整數次方LeetCode
- 【劍指offer】二進位制中1的個數
- 劍指Offer-39-數字在排序陣列中出現的次數排序陣列
- 劍指OFFER-數字在升序陣列中出現的次數(Java)陣列Java
- 給定一個n,輸出從1到n的整數
- 劍指offer第49題 醜數
- 劍指offer面試題(41-50)——java實現面試題Java
- LeetCode題解(Offer17):列印從1到最大的n位數(Python)LeetCodePython
- [劍指offer題解][Java]陣列中出現次數超過一半的數字Java陣列
- 劍指offer刷題之路--1.陣列中重複的數字陣列
- 劍指 Offer 53 - II. 0~n-1中缺失的數字(二分法)1
- 劍指Offer--面試題1:賦值運算子函式面試題賦值函式
- 劍指 Offer 15. 二進位制中1的個數
- 劍指offer(Java版)--將字串轉換為整數Java字串
- 劍指offer——把字串轉換成整數C++字串C++
- 「劍指offer」27道Mybatis面試題含解析MyBatis面試題
- 劍指Offer 表示數值的字串字串
- Leetcode 劍指 Offer 39. 陣列中出現次數超過一半的數字LeetCode陣列
- 【劍指offer中等部分4】二進位制中1的個數(java)Java
- 劍指Offer--陣列中重複的數字陣列
- 【劍指Offer】調整陣列順序使奇數位於偶數前面陣列
- 力扣 - 劍指 Offer 67. 把字串轉換成整數力扣字串
- 《Leetcode of December》劍指 Offer 67. 把字串轉換成整數LeetCode字串
- 力扣 - 劍指 Offer 39. 陣列中出現次數超過一半的數字力扣陣列
- 演算法 1~n中1的次數演算法
- LeetCode|劍指 Offer 49.醜數LeetCode
- 劍指Offer-把陣列中的數排成一個最小的數陣列
- 劍指offer面試題29:順時針列印矩陣面試題矩陣
- 劍指offer面試題11:旋轉陣列的最小數字(Java版已在牛客網AC)面試題陣列Java
- 劍指 Offer 44. 數字序列中某一位的數字