首先,這個確實不是標題黨,接下來我保證講的都是硬幹貨。也許有人會以爲很是偏很是難很是怪,可是我要說的是,對於技術社區來說,系統知識理論的學習自有去處,我以爲社區裏面應該注入一些新的血液,分享一些有信息量的內容,而不是將明明已經整理得很是好的知識點翻來覆去地「炒現飯」並以此來佔據社區最新文章欄。程序員
好,裝B結束。如今步入正題。框架
首先擺上題目:學習
摘自leetcode,本人最近剛買vip,所以看獲得企業的出題頻率:)ui
乍一看這一題貌似毫無頭緒,什麼是字典序?如何定位這個數?沒錯,剛接觸這個題目的時候,個人腦筋裏也是一團亂麻。spa
可是我以爲做爲一個擁有聰明才智的程序員來講,最重要的能力就是迅速抽象問題、拆解問題的能力。通過一段時間的思考,個人腦筋裏仍是沒有答案。3d
哈哈。指針
言歸正傳,咱們來分析一下這個問題。code
首先,什麼是字典序?cdn
簡而言之,就是根據數字的前綴進行排序,blog
好比10 < 9,由於10的前綴是1,比9小,
再好比112 < 12,由於112的前綴11小於12。
這樣排序下來,會跟日常的升序排序會有很是大的不一樣。先給你一個直觀的感覺,一個數乘10,或者加1,哪一個大?可能你會吃驚,後者會更大。
但其實掌握它的本質以後,你一點都不會吃驚。
畫一個圖你就懂了。
每個節點都擁有10個孩子節點,由於做爲一個前綴 ,它後面能夠接0~9這十個數字。並且你能夠很是容易地發現,整個字典序排列也就是對十叉樹進行先序遍歷。1, 10, 100, 101, ... 11, 110 ...
回到題目的意思,咱們須要找到排在第k位的數。找到他的排位,須要搞清楚三件事情:
接下來 ,咱們一一拆解這些問題。
如今的任務就是給定一個前綴,返回下面子節點總數。
咱們如今的思路就是用下一個前綴的起點減去當前前綴的起點,那麼就是當前前綴下的全部子節點數總和啦。
//prefix是前綴,n是上界
var getCount = (prefix, n) => {
let cur = prefix;
let next = prefix + 1;//下一個前綴
let count = 0;
//當前的前綴固然不能大於上界
while(cur <= n) {
count += next - cur;//下一個前綴的起點減去當前前綴的起點
cur *= 10;
next *= 10;
// 若是說剛剛prefix是1,next是2,那麼如今分別變成10和20
// 1爲前綴的子節點增長10個,十叉樹增長一層, 變成了兩層
// 若是說如今prefix是10,next是20,那麼如今分別變成100和200,
// 1爲前綴的子節點增長100個,十叉樹又增長了一層,變成了三層
}
return count;//把當前前綴下的子節點和返回去。
}
複製代碼
固然,不知道你們發現一個問題沒有,當next的值大於上界的時候,那以這個前綴爲根節點的十叉樹就不是滿十叉樹了啊,應該到上界那裏,後面都再也不有子節點。所以,count += next - cur仍是有些問題的,咱們來修正這個問題:
count += Math.min(n+1, next) - cur;
複製代碼
你可能會問:咦?怎麼是n+1,而不是n呢?不是說好了n是上界嗎?
我舉個例子,倘若如今上界n爲12,算出以1爲前綴的子節點數,首先1自己是一個節點,接下來要算下面10,11,12,一共有4個子節點。
那麼若是用Math.min(n, next) - cur會怎麼樣?
這時候算出來會少一個,12 - 10加上根節點,最後只有3個。所以咱們務必要寫n+1。
如今,咱們搞定了前綴的子節點數問題。
如今無非就是往子樹裏面去看。
prefix這樣處理就能夠了。
prefix *= 10
複製代碼
說白了,當前的前綴小了嘛,咱們擴大前綴。
prefix ++;
複製代碼
整合一下剛剛的思路。
let findKthNumber = function(n, k) {
let p = 1;//做爲一個指針,指向當前所在位置,當p==k時,也就是到了排位第k的數
let prefix = 1;//前綴
while(p < k) {
let count = getCount(prefix, n);//得到當前前綴下全部子節點的個數和
if(p + count > k) { //第k個數在當前前綴下
prefix *= 10;
p++; //把指針指向了第一個子節點的位置,好比11乘10後變成110,指針從11指向了110
} else if(p + count <= k) { //第k個數不在當前前綴下
prefix ++;
p += count;//注意這裏的操做,把指針指向了下一前綴的起點
}
}
return prefix;
};
複製代碼
/** * @param {number} n * @param {number} k * @return {number} */
var findKthNumber = function(n, k) {
let getCount = (prefix, n) => {
let count = 0;
for(let cur = prefix, next = prefix + 1; cur <= n; cur *= 10, next *= 10)
count += Math.min(next, n+1) - cur;
return count;
}
let p = 1;
let prefix = 1;
while(p < k) {
let count = getCount(prefix, n);
if(p + count > k) {
prefix *= 10;
p++;
} else if(p + count <= k) {
prefix ++;
p += count;
}
}
return prefix;
};
複製代碼
成功AC!