我的2020年leetcode刷題總結
學習 2021-01-01 11:346325閱讀 · 595喜歡 · 44評論
魔法小分隊隊長
粉絲:29.2萬文章:43
已關注
2020年過去了,我在leetcode上成功刷題1000+。
當然了,光追求刷題的數量是遠遠不夠的。我如果把每道題選一個高贊答案然后ctrl+c, ctrl+v,點擊提交,幾天也能做到“刷題”1000+,但這就沒有任何意義,別說別人了,自己都騙不了。
有人說了,我還沒無聊到這樣的地步,每道題代碼都是自己寫的。可這也是遠遠不夠,比如50題,讓你求一個數的pow(x,n),你如果直接寫一個pow函數,也能在系統里"solve"這道題,但是就失去了這道題的意義。
那如何才能叫真正solve這道題?那就是
1,如果讓你過一段時間,再讓你重新做這道題,不給你任何提示,你還能做出來嗎?
2,給你一道和這道題非常相像的姊妹題,你能順利想到思路,解出這道題嗎?
3,不給你任何編譯器,讓你直接在word文檔里寫出代碼,然后你把代碼copy到題目下面,點擊提交,能沒有任何語法錯誤,並且通過所有的test case嗎?
4,你能順利完成第三個要求,並且順便向周圍的朋友講明白你的思路,所有的過程都在半小時內完成嗎?(如果是在國外的話,你的英語能讓外國人也聽懂嗎?)
以上的四個要求,一個比一個高,我目前很多medium和hard題的水平只能滿足第2個,有幾道hard題目甚至連第一個都做不到。而真正的算法面試,是需要滿足第4個的。因此,我目前還遠沒有到面試的水平。當然,大家如果是剛剛刷題,不要直接要求4,否則很容易被勸退。
當然了,雖然我沒有達到這么高的要求,但是刷題1000+,總歸是有收獲的。我的最大收獲是,我不再畏懼寫代碼,甚至很享受它。
而且,因為經常參加周賽,我的編程水平確實有質的提升,在刷leetcode之前我雖然也會寫代碼,但是寫出來的代碼又臭又長(比如編程實現掃雷那個,現在看真是各種捂臉),而且我還以自己寫了多少行為榮,完全不考慮任何的優化。
現在呢,不管是寫什么樣的代碼,雖然不能保證一遍過,但是絕大多數時候只需要做一小點微調就可以了,不像以前,調bug的時間比寫代碼的時間還長。
還記得我第一次參加周賽的時候,自己費勁巴力只做出了第一題,排名在后50%,雖然比較喪氣,但是我想:能做出一道題,說明我還是比那些完全不會編程的人強一點。然后后來,我就能做出一道easy難度一道medium難度的題目了。練習了300多道之后,我在某場周賽成功在最后幾分鍾拿下了最后一道題,那是人生第一次AK (all killed,表示全部通過)。雖然那場周賽很簡單,1000多人都AK了,但是我還是很激動,畢竟人生第一次。當然,下一場比賽我就被打回原形了,印象中只做出了兩道題。這也告訴我,永遠不要認為你已經掌握算法,一次比賽表現好只是偶然,10次平均值才能說明問題。
現在呢,雖然我的成績還是有起有落,在兩周前的比賽還拿過1000多名(每次周賽參賽人數大約1萬人),但是整體肯定是比以前好很多了,一般都至少能做出三題,時不時還能進個前200甚至前100,拿到獎勵的leetcode coins。新年最后一次比賽,我的名次是156名,這讓我的rating總排名進入了前1000。這是我的曲線,整體還是向上的。
我的周賽排名
說了這么多我刷題的經歷,現在來分享一下我是如何做到的。
首先,大家剛拿到leetcode的時候,肯定都是躊躇滿志,想着:“看我把它刷爆”。第一題,two sum,嗯嗯,簡單,我一個二重循環就通過了。提交之后,絲毫沒有注意到自己的時間只擊敗了5%的人,然后第二題......鏈表,這是什么玩意?放棄。然后第三題,longest substring....什么是substring?放棄。第四題一看是hard就直接跳過。之后就開始浮躁起來,覺得自己怎么這么笨,啥都不會,只會一些比如50題這樣的一行代碼就能過的題目(但是第50題意圖肯定不是讓你一行代碼解決,而是考察你快速冪的知識,直接用pow過了沒有任何意義),最后只做了少數的幾道題。
如果你是上面的過程,不用氣餒,因為一開始我就是這樣的,這個時候,應該怎么做呢?
要做的是,迅速承認自己智商不足的事實。我不是高斯那樣的天才,很多優美的算法,其實是數學家和計算學家研究了很長時間的,我怎么可能無師自通?要做的就是,第一次做不出來不丟人,要看別人的高贊解法。大家都是這么過來的。
比如第三題,需要用到sliding window算法,但是很多只學過數據結構,甚至只學過C語言的人,怎么可能知道sliding window?這個時候,就是要看別人的解法,看完之后自己嘗試着去寫。然后,通過以后,點擊sliding window這個標簽,會出現很多類似的題目,找一道簡單難度的自己去做。按照tag刷標簽是很高效的。
如果發現類似的題目還是不會刷怎么辦?這也很正常,因為很多medium或者hard難度的題目需要同時用到多種方法,你要的方法,只是其中一環,而且往往還不是第一環,你無法轉化成自己能解決的問題,自然就沒法做。這樣的題目往往是medium-hard難度。
另外,做題就一定要考慮的是時間復雜度。leetcode后期的題目,都會告訴你各個變量的取值范圍,這個是非常重要的信息點,如果你題目做多了,就會發現,取值范圍其實就暗示着作者想讓你用什么樣的算法。
N~16: 可以用指數O(2^N)的算法
N~100: 指數算法會爆炸,必須要用多項式算法。
N~500: 時間復雜度不能高於O(N^3)
N~2000: 時間復雜度不能高於O(N^2)
N~10^5: 時間復雜度不能高於O(N log N)
N~10^9: 時間復雜度必須為O(log N)或者 O(sqrt(N))
有那么一個公式,就是你的總計算次數不能超過10^7數量級,否則就會超時。這個在任何的編程競賽中都適用。
另外,如果是多個變量,題目描述完全不變,改變變量的取值范圍,就完全是不同的題目。比如數組的長度為M,每個元素取值范圍是0-N,同樣是分割成和相等的若干子數組,如果M很短,N很大,這可能就是一道backtrack問題,需要遞歸,但如果M很長,但每個元素取值只能到0-10,這就是一個背包問題,需要動態規划。所以有這么種說法,不給變量的范圍就是耍流氓。
除了時間復雜度,還要看你的算法在所有方案中位置。很多人說一定要擊敗90%的人,其實沒必要,你只要注意一下前面有沒有一個高峰,並且峰值時間比你的一半還少就行。python算法不太穩定,12ms和16ms沒什么區別,可能你上次提交擊敗50%,下次提交就95%了。
我在做題的時候,一開始我就是照抄別人的算法,一行一行理解,有些關鍵的代碼,比如二分法的邊界判定,比如union find,比如樹狀數組,則在理解的基礎上直接背過。另外,如果長時間做不出來,則直接標記,過一個月再刷一下。
對於大家都頭疼的dynamic programming,這種題目變化多樣,很容易讓大家懷疑自己的智商。說實話我一開始做第10題,一天都沒做出來,看了代碼都看不懂,后來我發現,這種DP問題,如果用遞歸+存儲狀態方法的話,能更好的理解。遞歸+存儲狀態又稱記憶化搜索,我只處理現在的情況,然后下一步直接調用自身。然后還要用一個多維數組或者字典來存儲狀態避免重復計算(這種記憶化在bfs中應用也極為廣泛)。比如下面一個例子,求某個路徑和最大值類。
dp = [[0 for j in range(n)] for i in range(m)]
def getnext(a,b):
if a>=m or b>=n: return 0
if dp[a][b] >0: return dp[a][b]
num1 = ...... + getnext(a+1,b)
num2 = ....... + getnext(a,b+1)
dp[a][b] = max(num1,num2)
return dp[a][b]
用這種思想,在周賽中遇到DP類題,只要不是那種只有幾十個人通過的變態難題,我基本都能解出來。
另外一種是堆(heap)問題,大家一定要重視,這種算法和BFS和DFS是同等重要的,BFS的實現是隊列,先進先出,DFS實現是棧,先進后出,而堆則是不管什么時候進,都是最小(最大)出。我用堆這個思想,順利解出了一大批leetcode的難題,而且對於最小生成樹,最短路徑問題,天際線問題等等,用heap簡直不能再爽。大家一定要練好。
用這些方法,我刷題數量越來越多,而且參加周賽真的是對你編程的一次檢驗,周賽積分上升真的是正反饋,如果沒有周賽的話我真的很難刷的下去。所以建議大家一定要多參加幾次周賽,這樣讓你的刷題不再單調。
通過tag訓練法,再加上周賽的激勵,我實現了1000+刷題,接下來有什么目標呢?再追求2000+題目也沒什么意義,因為我刷的題目已經夠多了,現在就是要追求質量,爭取我刷過的題目,都能一遍過,沒有bug,而且講明白。
而且,是時候把重心放回到其他方面了。比如我整個冬天都沒怎么更新視頻,再不更新大家就該把我忘了。
最后祝大家2021年心想事成!