剑指 Offer 43. 1~n 整数中 1 出现的次数(可能是最简洁易懂的)
今天我們來看一道賊棒的題目,題目不長,很經典,也很容易理解,我們一起來看一哈吧,
大家也可能做過這道題,那就再復習一下,如果沒做過的話,可以看完文章,自己去 AC 一下,不過寫代碼的時候,要自己完全寫出來,這樣才能有收獲,下面我們看題目吧。
leetcode 233. 數字 1 的個數
題目描述
給定一個整數 n,計算所有小于等于 n 的非負整數中數字 1 出現的個數。
示例 1:
輸入:n = 13
輸出:6
示例 2:
輸入:n = 0
輸出:0
太喜歡這種簡潔的題目啦,言簡意賅,就是讓咱們找出小于等于 n 的非負整數中數字 1 出現的個數。
大家看到這個題目的第一印象,可能會這樣想,哦,讓我們求 1 的個數。
吶我們直接逐位遍歷每個數的每一位,當遇到 1 的時候,計數器 +1,不就行了。
嗯,很棒的方法,可惜會超時。(我試了)
或者說,我們可以先將所有數字拼接起來,然后再逐位遍歷,這樣仍會超時。(我也試了)
大家再思考一下還有沒有別的方法呢?
既然題目讓我們統計小于等于 n 的非負整數中數字 1 出現的個數。
那我們可以不可這樣統計。
我們假設 n = abcd,某個四位數。
那我們完全可以統計每一位上 1 出現的次數,個數上 1 出現的次數,十位上 1 出現的次數,百位 ,千位。。。
也就是說小于等于 n 的所有數字中,個位上出現 1 的次數 + 十位出現 1 的次數 + 。。。最后得到的就是總的出現次數。
見下圖
我們假設 n = 13 (用個小點的數,比較容易舉例)
我們需要統計小于等于 13 的數中,出現 1 的次數,
通過上圖可知,個位上 1 出現 2 次,十位上 1 出現 4 次
那么總次數為 2 + 4 = 6 次。
另外我們發現 11 這個數,會被統計 2 次,它的十位和個位都為 1 ,
?
而我們這個題目是要統計 1 出現的次數,而不是統計包含 1 的整數,所以上訴方法不會出現重復統計的情況。
我們題目已經有大概思路啦,下面的難點就是如何統計每一位中 1 出現的次數呢?
我們完全可以通過遍歷 n 的每一位來得到總個數,見下圖
假設我們想要得到十位上 1 出現的次數,當前我們指針指向十位,
我們稱之為當前位。num 則代表當前位的位因子,當前位為個位時 num = 1,十位時為 10,百位時為 100…
那我們將當前位左邊的定義為高位,當前位右邊的定義位低位。
例:n = 1004 ,此時指針指向十位(當前位)num = 10,高位為百位,千位,低位為個位
而且我們某一位的取值范圍為 0 ~ 9,那么我們可以將這 10 個數分為 3 類,小于 1 (當前位數字為 0 ),等于 1(當前位數字為 1 ) ,大于 1(當前位上數字為 2 ~ 9),下面我們就來分別考慮三種情況。
我們進行舉例的 n 為 1004,1014,1024。重點討論十位上 3 種不同情況。大家閱讀下方文字之前,先想象自己腦子里有一個行李箱的滾輪密碼鎖,我們固定其中的某一位,然后可以隨意滑動其他位,這樣可以幫助大家理解。
?
注:該比喻來自與網友 ryan0414,看到的時候,不禁驚呼可太貼切了!
n = 1004
我們想要計算出小于等于 1004 的非負整數中,十位上出現 1 的次數。
也就是當前位為十位,數字為 0 時,十位上出現 1 的次數。
解析:為什么我們可以直接通過高位數字 * num,得到 1 出現的次數
?
因為我們高位為 10,可變范圍為 0 ~ 10,但是我們的十位為 0 ,所以高位為 10 的情況取不到,所以共有 10 種情況。
?
又當前位為十位,低位共有 1 位,可選范圍為 0 ~ 9 共有 10 種情況,所以直接可以通過 10 * 10 得到。
其實不難理解,我們可以設想成行李箱的密碼盤,在一定范圍內,也就是上面的 0010 ~ 0919 , 固定住一位為 1 ,只能移動其他位,看共有多少種組合。
好啦,這個情況我們已經搞明白啦,下面我們看另一種情況。
n = 1014
我們想要計算出小于等于 1014 的非負整數中,十位上出現 1 的次數。
也就是當前位為十位,數字為 1 時,十位上出現 1 的次數。
我們在小于 1014 的非負整數中,十位上為 1 的最小數字為 10,最大數字為 1014,所以我們需要在 10 ~ 1014 這個范圍內固定住十位上的 1 ,移動其他位。
其實然后我們可以將 1014 看成是 1004 + 10 = 1014
則可以將 10 ~ 1014 拆分為兩部分 0010 ~ 0919 (小于 1004 ),1010 ~ 1014。
見下圖
解析:為什么我們可以直接通過 高位數字 * num + 低位數字 + 1 即 10 * 10 + 4 + 1
?
得到 1 出現的次數
?
高位數字 * num 是得到第一段的次數,第二段為 低位數字 + 1,求第二段時我們高位數字和當前位已經固定,
?
我們可以改變的只有低位。
可以繼續想到密碼盤,求第二段時,把前 3 位固定,只能改變最后一位。最后一位最大能到 4 ,那么共有幾種情況?
n = 1024
我們想要計算出小于等于 1024 的非負整數中,十位上出現 1 的次數。
也就是當前位為十位,數字為 2 ~ 9 時,十位上出現 1 的次數。其中最小的為 0010,最大的為 1019
我們也可以將其拆成兩段 0010 ~ 0919,1010 ~ 1019
解析:為什么我們可以直接通過高位數字 * num + num, 10 * 10 + 10 得到 1 出現的次數
?
第一段和之前所說一樣,第二段的次數,我們此時已經固定了高位和當前位,當前位為 1,低位可以隨意取值,上訴例子中,當前位為 10,低位為位數為 1,則可以取值 0 ~ 9 的任何數,則共有 10 (num) 種可能。
好啦,下面我們看一下題目代碼吧
注:下方代碼沒有簡寫,也都標有注釋
class Solution {public int countDigitOne(int n) {// 高位int high = n;// 低位int low = 0;// 當前位int cur = 0;int count = 0;int num = 1;while(high != 0 || cur != 0) {cur = high % 10;high /= 10;// 這里我們可以提出high * num因為我們發現無論為幾,都含有它if(cur == 0) count += high * num;else if(cur == 1) count += high * num + 1 + low;else count += (high + 1) * num;// 低位low = cur * num + low;num *= 10;}return count;} }時間復雜度 : O(logn) 空間復雜度 O(1)
總結
以上是生活随笔為你收集整理的剑指 Offer 43. 1~n 整数中 1 出现的次数(可能是最简洁易懂的)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【最佳解法】剑指 Offer 42. 连
- 下一篇: 剑指 Offer 44. 数字序列中某一