總結(jié)我做nowcoder oj上劍指offer的66道題時的一些感想(分三天回顧一下)。總得來說這66題大部分還不錯,字符串,鏈表,樹,堆,棧,雙端隊列,動態(tài)規(guī)劃,回溯,二分搜索都有涉及,但是題量還是小了些,比如回溯,初次接觸的人可能會一臉懵逼。國內(nèi)公司面試應(yīng)該不會超出這個程度。
代碼放在了github。
1. 二維數(shù)組中的查找(4) [打分: 5/10]
在一個每一行每一列都遞增的二維數(shù)組里找一個數(shù)。trick是從左下角(或者右上角)開始找。
注意,這題在leetcode也有,同時有個類似題目:如果上一行最后一個小于下一行第一個數(shù)字怎么做?答案是binary search。
替換空格(5) [打分: 3/10]
這題考的是c語言操作數(shù)組的技巧,c語言的話要先traverse一遍看有多少空格,然后定義新數(shù)組長度為oldnumber+replacenumber*2;Java可以用StringBuffer
構(gòu)造或者直接用String.replaceAll
。從尾到頭打印鏈表(6)[打分: 9/10]
這題要能給出多種思路。
Approach 1. 逆序鏈表。代碼略
Approach 2. 用棧,空間O(n)。代碼略
Approach 3. 遞歸,每次先打印后一個節(jié)點的值,缺點是可能stack overflow重建二叉樹(7)[打分: 8/10]
思路是定位root在inorder的數(shù)組中的位置,遞歸構(gòu)建。用兩個棧實現(xiàn)隊列(9)[打分: 7/10]
代碼短但是有一定的技巧。值得再做。旋轉(zhuǎn)數(shù)組的最小數(shù)字(11)[打分: 8/10]
Leetcode高分題search in rotated sorted array, 要點是找到一個ascending的區(qū)間進行搜索。值得做。斐波那契數(shù)列(10) [打分: 7/10]
經(jīng)典動態(tài)規(guī)劃(DP),早年fb面實習(xí)生簽到題。跳臺階[打分: 7/10]
經(jīng)典DP,思路同斐波那契。leetcode原題。變態(tài)跳臺階[打分: 7/10]
上一題的followup。矩形覆蓋[打分: 7/10]
同樣是斐波那契數(shù)列。轉(zhuǎn)移方程: dp [n] = dp[n-1] + dp[n - 2]二進制中1的個數(shù)(15)[打分: 6/10]
位運算。常規(guī)做法是對每bit都右移和1相與;更好的做法是把這個數(shù)持續(xù)地減去1并且與原來的數(shù)按位與運算(相當(dāng)于把原來的數(shù)最右邊的1變成0),然后重復(fù)這一操作。數(shù)值的整數(shù)次方(16)[打分: 6/10]
首先要考慮corner case;其次用遞歸求解更快。調(diào)整數(shù)組順序使奇數(shù)位于偶數(shù)前面(21)[打分: 7.5/10]
有點巧妙,類似插入排序(穩(wěn)定)。不要用額外空間。鏈表中倒數(shù)第k個結(jié)點(22)[打分: 7/10]
快慢指針,寫起來需要注意細(xì)節(jié)。
15.反轉(zhuǎn)鏈表(24)[打分: 7.5/10]
經(jīng)典老題。迭代好寫,遞歸難寫。
合并兩個排序的鏈表(25)[打分: 7.5/10]
leetcode原題,去東京的飛機上寫過。迭代好寫,遞歸難寫。樹的子結(jié)構(gòu)(26) [打分: 7.5/10]
leetcode原題。子樹和子結(jié)構(gòu)不同。二叉樹的鏡像(27) [打分: 7/10]
難倒brew作者的easy題。順時針打印矩陣(29) [打分: 3/10]
沒啥意思的一道題。包含min函數(shù)的棧(30) [打分: 7/10]
跟第5題兩個棧實現(xiàn)一個隊列一樣有點tricky。棧的壓入、彈出序列(31) [打分: 8/10]
借助一個棧來模擬操作。有一定思維技巧,值得重復(fù)做。leetcode946題。從上往下打印二叉樹(32) [打分: 8/10]
BFS。送分題。不談了。二叉搜索樹的后序遍歷序列(33) [打分: 8/10]
BST的后序序列的合法序列是,對于一個序列S,最后一個元素是x (也就是根),如果去掉最后一個元素的序列為T,那么T滿足:
T可以分成兩段,前一段(左子樹)小于x,后一段(右子樹)大于x,且這兩段(子樹)都是合法的后序序列。完美的遞歸定義 : )二叉樹中和為某一值的路徑(34) [打分: 8/10]
一道回溯題。跟BinaryTreePaths和CombinationSum的解法類似。需要注意的就是target的判斷和不要多remove一次。復(fù)雜鏈表的復(fù)制(35)[打分: 6/10]
leetcode原題。解法比較固定。
第一步,在舊鏈表中創(chuàng)建新鏈表,此時不處理新鏈表的next和random(因為random指向的節(jié)點可能還沒初始化出來)
第二步,初始化新鏈表的random
第三步,分離兩個鏈表二叉搜索樹與雙向鏈表(36) [打分: 6/10]
把BST轉(zhuǎn)換成雙向鏈表。這題我不是很理解,也沒說要返回head。它的意思大概是說把tree.left當(dāng)做node.prev吧,那就中序遍歷,把cur.left指向prev,prev.right指向cur。這題好像是leetcode鎖住的一道題。字符串的排列(38) [打分: 10/10]
leetcode原題。經(jīng)典backtracking。數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字(39)[打分: 7/10]
這題是典型的有點tricky的題,要記住。借助額外空間當(dāng)然簡單但是不是面試官想看到的答案。leetcode也有,摩爾投票法。最小的K個數(shù)(40) [打分: 10/10]
PriorityQueue與最小堆的應(yīng)用。連續(xù)子數(shù)組的最大和(42) [打分: 9/10]
經(jīng)典DP。Leetcode53題。Maximum SubArray。整數(shù)中1出現(xiàn)的次數(shù)(從1到n整數(shù)中1出現(xiàn)的次數(shù))(43) [打分: 0/10]
這題直接放棄了 Math的題,實在不想看。LeetCode 233題。把數(shù)組排成最小的數(shù)(45) [打分: 7/10]
這題就是LeetCode 179. Largest Number。簡單方法用一個Comparator來比較o1 + o2和o2 + o1的大小就行。另有一種奇淫巧技。丑數(shù)(49) [打分: 7.5/10]
這題是leetcode 264. Ugly Number II。brute force會TLE。要用DP。第一個只出現(xiàn)一次的字符位置(50) [打分: 4/10]
用Map就行。至少要遍歷一次。這題leetcode也有,一道easy題。數(shù)組中的逆序?qū)?51) [打分: 7/10]
考察merge sort。在merge sort基礎(chǔ)上加一行即可。值得做。兩個鏈表的第一個公共結(jié)點(52) [打分: 8/10]
方法1,Map,空間O(n),不太好。
方法2,找出2個鏈表的長度,然后讓長的先走兩個鏈表的長度差,然后再一起走。值得重復(fù)做。數(shù)字在排序數(shù)組中出現(xiàn)的次數(shù) [打分: 8/10]
重復(fù)進行二分搜索。另有新穎解法: +-0.5。另外我覺得這題可以直接用c++的upper_bound和lower_bound來做。二叉樹的深度(55) [打分: 7/10]
DFS或者BFS都行。做爛了。平衡二叉樹 [打分: 7.5/10]
這題也比較tricky,要post order,否則多走很多路。數(shù)組中只出現(xiàn)一次的數(shù)字 [打分: 6/10]
這題考察XOR操作。和為S的連續(xù)正數(shù)序列[打分: 9/10]
滑動窗口法。值得做。和為S的兩個數(shù)字(57)[打分: 8/10]
這題跟two sum的區(qū)別就是這題是sorted array,所以可以用two pointers。左旋轉(zhuǎn)字符串(10)[打分: 7/10]
用substring就行。今天看到題解,是用類似旋轉(zhuǎn)整個字符串然后再部分翻轉(zhuǎn)的的思路;比如hello world單詞翻轉(zhuǎn),也可以看成是這題的特殊情況,從空格翻轉(zhuǎn)。這樣做更像是trick,唯一的好處就是不借助額外空間。翻轉(zhuǎn)單詞順序列(10)[打分: 7.5/10]
這題9月面試的時候問到,我直接用split來做,面試官一直提示我直接操作字符;現(xiàn)在想想他想看的是劍指offer上的解法,先翻轉(zhuǎn)每個單詞,再翻轉(zhuǎn)整個句子;或者先翻轉(zhuǎn)整個句子,再翻轉(zhuǎn)每個單詞。
證明一件事情:無論你想的答案復(fù)雜度如何,面試官想看的永遠(yuǎn)是他看過的那個答案。撲克牌順子(61)[打分: 9/10]
我重新描述一下題目: 給一個數(shù)組里面有5個非負(fù)整數(shù),其中0可以看做任何數(shù),問這五個數(shù)能否組成連續(xù)序列。解法是one pass hashset(one pass兩個條件還挺巧妙的))。不用排序。孩子們的游戲(圓圈中最后剩下的數(shù))(62)[打分: 8/10]
約瑟夫環(huán)問題。
編號0 ~ n - 1的小朋友圍成圓圈,從0開始依次數(shù)m - 1個數(shù),第m - 1個不再參與游戲;然后下一個人繼續(xù)從0開始數(shù),求最后剩下的人的序號。
用Java的話可以用linkedlist模擬(不用真地首尾連起來,只要用mod計算循環(huán)index),可以模擬remove。linkedlist比arraylist插入刪除效率高,因為不用arraycopy。也可以用數(shù)組。求1+2+3+...+n(64)[打分: 3/10]
這題看了下書上,本意是用一些c++特性來求解。用Java的話可以用遞歸和&&。不用加減乘除做加法(65)。[打分: 3/10]
寫一個函數(shù),求兩個整數(shù)之和,要求在函數(shù)體內(nèi)不得使用+、-、*、/四則運算符號。方法是位運算。把字符串轉(zhuǎn)換成整數(shù)(67)[打分: 5/10]
這題是leetcode第8題。那題差評率高。解法沒啥意思,就是按位累乘。數(shù)組中重復(fù)的數(shù)字[打分: 8/10]
approach1, sort 復(fù)雜度高。
approach2, map/set 需要額外空間。
approach3, inplace substitution。無需額外空間,利用「長度為n的數(shù)組里的所有數(shù)字都在0到n-1的范圍內(nèi)」這個條件。思路是把nums[nums[i]]上的數(shù)字+length,這樣下次如果發(fā)現(xiàn)nums[nums[i]]已經(jīng)>=length了,說明已經(jīng)加過一次了。有一定思維難度。構(gòu)建乘積數(shù)組(66)[打分: 7/10]
這題要畫圖(上三角和下三角)才能做出來。有一定的思維技巧。正則表達(dá)式匹配 [打分: 7/10]
劍指offer上唯一一道hard題。對應(yīng)leetcode第10題。遞歸求解。表示數(shù)值的字符串[20] [打分: 0/10]
很沒價值的一題。如果這題因為漏了corner case而不能AC,無需責(zé)備自己。
其實可以看下力扣用自動機的解法。字符流中第一個不重復(fù)的字符 [打分: 5/10]
這題要用額外空間。我用了LinkedHashMap
,然后對map.entrySet()
做遍歷。也可以直接用HashMap
。鏈表中環(huán)的入口結(jié)點(23)[打分: 7/10]
最簡單的方法,找第一個重復(fù)的內(nèi)存,需要O(n)空間。
劍指offer上的解法, 先用快慢指針判斷是否有環(huán),然后計算環(huán)的長度n,然后再用兩個指針,一個先走n步,一個從頭開始走,他們相遇的結(jié)點就是entry node。
leetcode142、287題有快慢指針方法。刪除鏈表中重復(fù)的結(jié)點(18)[打分: 7/10]
runner和walker操作。
另一題:O(1)時間刪除鏈表結(jié)點。注意corner case。二叉樹的下一個結(jié)點(8)[打分: 7//10]
給定一個二叉樹和其中的一個結(jié)點,請找出中序遍歷順序的下一個結(jié)點并且返回。注意,樹中的結(jié)點不僅包含左右子結(jié)點,同時包含指向父結(jié)點的指針。naive做法當(dāng)然可以從頭開始中序遍歷,但是效率不高。所以要找規(guī)律;如果有右子樹,則找右子樹的最左節(jié)點;如果沒有右子樹,則找第一個當(dāng)前節(jié)點是父節(jié)點左孩子的節(jié)點(因為當(dāng)前節(jié)點可能是父節(jié)點的右子樹)。對稱的二叉樹(28)[打分: 8/10]
代碼很巧妙,DFS或BFS都行。leetcode有。按之字形順序打印二叉樹[打分:7/10]
BFS送分題。
把二叉樹打印成多行[打分:7/10]
BFS送分題。序列化二叉樹(37)[打分: 8/10]
leetcode經(jīng)典題,第二道hard。二叉搜索樹的第k個結(jié)點(54)[打分: 7.5/10]
inorder DFS,或者更巧妙地,計算右子樹節(jié)點個數(shù),類似二分。數(shù)據(jù)流中的中位數(shù)(41)[打分: 9/10]
這題非常巧妙,用兩個PriorityQueue
.
滑動窗口的最大值[打分: 10/10]
很巧妙,用ArrayDeque
。矩陣中的路徑(12)[打分:9/10]
leetcode的word search。經(jīng)典Backtracking。backtracking題要注意有的回溯時不需要重置標(biāo)志位,比如word island.機器人的運動范圍(13)[打分:10/10]
同樣是一道backtracking。注意不要重置標(biāo)志位。
以下是牛客oj沒有的題
剪繩子(14)
題意:把一個整數(shù)分成至少兩份,返回最大可能的乘積值。
這題很好,是劍指offer第14題,剪繩子。可以用動態(tài)規(guī)劃和貪心兩種方式來做。其中動態(tài)規(guī)劃有可以用記憶化來做。打印1到最大的n位數(shù)(17)
亞麻onsite題目。LCA問題
數(shù)字翻譯成字符串(46)
top down recursion with memo或者bottom up dp,leetcode好像有
dp[i] = dp[i + 1] + substring(i, i + 2) < 26 ? dp[i + 2] : 0n個骰子的點數(shù)(https://www.cnblogs.com/AndyJee/p/4686208.html)
https://www.cnblogs.com/xuanxufeng/p/6896569.html
在c-1個骰子的基礎(chǔ)上,再增加一個骰子出現(xiàn)點數(shù)和為k的結(jié)果只有6種情況:
dp(c,k)=dp(c-1,k-1)+dp(c-1,k-2)+dp(c-1,k-3)+dp(c-1,k-4)+dp(c-1,k-5)+dp(c-1,k-6)(注意當(dāng)k<6時的處理越界問題)
有1個骰子,dp(1,1)=dp(1,2)=dp(1,3)=dp(1,4)=dp(1,5)=dp(1,6)=1
https://www.e-learn.cn/content/qita/2293425