計算機行業細分下來領域其實很多,系統、網路、硬體、前後端、演算法等等。但是不管我們處於哪個細分領域,找工作第一件事就是刷題。演算法題也是面試中讓很多人頭疼的部分,主要難點有兩個:
知識點類別多。演算法題涉及到的知識點非常廣泛,比如陣列,連結串列,樹,圖,排序,貪心等等。
知識點難度大。除了類別多之外,有些題目也特別難,代表就是力扣中眾多的 Hard 難度的題目。
除此以外,還有一種情況是有的時候明明已經刷了很多題了,但是面試的時候還是腦子一片空白,對一些相似題型做不到舉一反三。這說明刷題也不是盲目無腦平推就行,除了講量,還要講方法。尤其是對演算法相關知識不是特別熟練的同學,掌握一些好的刷題策略是非常重要的。我們先來聊一聊學習演算法的一些策略和常見誤區。
一、AC 只是開始應該如何判斷自己的實力呢?相信大家都在力扣上得到過很多的 Accept,也就是 AC。假如來了一道題,可以假想這是面試官給的題目,我們這裡可以簡單地將解答分為四個級別:
第一個級別,我們磕磕絆絆或者靠著面試官的提示寫出了正確的解法;第二個級別,我們快速寫出了正確的解法,能夠完美應對所有的測試資料,包括可能的邊角資料(Corner Case);第三個級別,我們快速寫出了正確的解法,並且寫出來的是最優解法,一般題目對於時間複雜度考察比較多,這裡預設是最優的時間複雜度;第四個級別,我們的程式碼風格非常優美,變數名含義清晰,過程清楚明白,有一些簡單的註釋。有時候我們可能發現面試的時候明明都寫對了,最後還是沒透過,這就有可能是你的程式碼風格沒有讓面試官滿意。所以,在平時刷題的時候,我們也需要注意這幾個方面的內容,而不是看到 AC 就不管了。千萬牢記,AC 只是開始。
二、規劃刷題過程
力扣上大概有快 2000 題了,但是大部分人備考演算法的時間可能只有 2 個月左右。在這麼短的時間內要刷完所有題並且掌握是很困難的,實際上也不是非常必要。力扣的題目有難度區分,不同力扣題的定位是不一樣的。甚至在不同的人看來,作用也是不一樣的,對於不同的人,刷題的策略也應該有所區別。
小知識:力扣的題目越往前越經典,相對來說被考查的機率也越高。
演算法新手
首先,如果我們是剛接觸演算法題目,這時可以按照不同的專題分專題來刷題,比如動態規劃專題,棧專題等等。我們需要儘量吃透每一個專題的內容。量的話可以每一個專題完成 60% Easy 的題目,選擇並完成大概 30% 左右的 Medium 題目,Hard 題目的話可以挑少量進行嘗試,不過不用苛求。在這個階段,刷題的重心在於 確保自己理解每道題的解法,並能獨立快速正確地寫出程式碼。
有一定演算法積累
然後,如果我們對演算法比較熟悉,或者已經完成了第一個階段,這時就需要大量地綜合鞏固我們的知識脈絡和手感。這個階段也就是我們說的「無腦刷題」階段。不過刷題也並不是完全無腦,題目我們可以按順序刷也可以隨機刷,但是一定要注意這個階段的重心是不一樣的,我們要確保 每道題的解法都是最優的,並且 確保快速。這裡可以給一個簡單的標準:對於大多數 Easy 題,從讀題到 AC 不超過 8 分鐘;對於 Medium 題,從讀題到 AC 不超過 30 分鐘(時間僅供參考,不過相信自己,實際上你可以做到比這個快很多)。如果有餘力,我們可以進一步注意我們的程式碼風格,如變數命名、易讀性等問題。刷題方式是建議每天先不停地刷 Easy,注意保持 AC 節奏和速度。然後最後做幾道 Medium 收尾。
能輕鬆解決 Easy 和 Medium
最後,如果前面的部分已經完成得很好了,剩下的就是繼續拔高的過程。這裡主要是兩個方向:一個是難題和新知識點,我們可以接觸一些高階的演算法和資料結構,如線段樹、矩陣乘法、網路流、程式碼量大的模擬題等。
小技巧:在做題的過程中,我們也需要思考題目可能的變形和擴充套件。這個主要推薦在第一和第三階段做,第二階段主要還是訓練速度和準確率。一方面,在實際面試過程中經常會碰到原題被包裝了一下,或者稍微進行了改動;另一方面,這也是對自己很好的鍛鍊和拔高,這個過程對於新手還比較困難。因此,課程也會提供題目的不同變形和擴充套件,從易到難不等,幫助大家建立舉一反三的好習慣。
三、演算法進階
在有了一定的演算法基礎以後,可以提升一下應試能力,主要是在高壓下的做題能力,這裡推薦力扣每週舉辦的周賽和每兩週舉辦的雙週賽,比賽持續一個半小時,時間一般為每週日的早上 10:30 開始或者是週六晚上 22:30 開始。題目全部為之前未出現過的新題,一般前兩題不涉及具體演算法或涉及一些簡單的演算法思想,後兩題則可能考察各方面的演算法能力。簡單來說,相比於自己刷題,參加周賽有以下幾點顯而易見的區別或者優勢:
檢驗演算法能力: 不同於自由刷題的輕鬆環境,現在需要在一個半小時內 AC 四道題目,這無疑對我們的演算法思維敏捷、清晰程度,以及程式碼能力(包括速度和準確性)都有了很高的要求。磕磕絆絆地 AC 題目,雖然可以滿足平時的刷題需求,但是實際上是滿足不了周賽,或者說實際面試時的要求的。根據每次周賽的名次,以及 AC 題目的數量,我們可以粗略地量化我們的演算法實力。這樣能直觀的發覺這段時間的訓練是否有帶來提升,或者還有哪些不足的知識點和內容需要補充。增加抗壓能力: 限時和比賽的另一個好處是能營造一種緊張的氛圍,幫助我們集中注意力。雖然比不上正式面試的環境,但也是一種良好的過渡。在經歷了多次比賽的洗禮後,再碰到類似的情景(例如筆試),我們能夠有更加良好的心態來面對。新人入賽周賽排名規則如果你是一名競賽新人,我們不妨開啟最新的一場周賽排名,然後一起來了解一下週賽的具體排名規則。
一場周賽最多可能有近萬人報名,但是題目只有四道,如何對選手進行排名呢?這裡主要是透過三個方面來進行的。
題目積分。 根據難易程度,不同的題目有不同的分數,每透過一道題目均能得到相應的題目分數。當兩個選手分數不一致時,分數高(即過的題多)的選手顯然名次更靠前。時間。 同樣的題目,在兩個人都透過的情況下,一個人用時 10 分鐘,另一個人用時 1 小時,顯然也是用時短的更優。因此,在分數一致的情況下,透過最後一題時間更靠前的選手,排名更靠前。錯誤次數。 由於寫程式碼不可避免地會碰到出 bug 的情況,相同條件下顯然出 bug 次數少甚至沒有 bug 的選手是更厲害的,即程式碼能力更強。為了量化程式碼準確度,每出現一次錯誤的提交(包括但不限於答案錯誤,記憶體超限,時間超限等等),如果後續題目通過了,之前的每次錯誤提交均會在最終透過時間上增加 5 分鐘的懲罰。正是由於錯誤懲罰機制,大家在提交答案之前一定要仔細檢查,避免不必要的罰時。
周賽爆 0 怎麼辦?如果大家是第一次參加周賽,那麼只做出來一道題甚至一道題都沒做出來都是很正常的。例如下列常見場景:
題目似乎簡單,但是腦子裡想法錯了導致實現一直出問題。看錯題目問題,平白浪費時間。複雜度計算錯誤,一直超時。...以上任何一個場景都可能會導致我們周賽成績不佳,甚至 0 題透過。實際上類似”腦子短路”的問題在平時刷題的時候我們可能也會出現,但是那時基本很難意識到這些情況的發生,因為我們總是能求助各種題解和程式碼。而在限時壓力的逼迫下,這些小問題的影響就會逐漸放大,導致我們發揮不出應有的水平。
所以一方面我們需要不斷參賽鍛鍊自己的心理素質,另一方面就算某次成績不佳我們也不需要因此自責,只需要反過來思考這場比賽中發現的問題即可,例如:
是否有漏掉沒掌握的知識點?是否某些題目思路想的過於複雜,增加了實現難度?是否當時太緊張導致思路不清晰?是否某些題目其實還沒想清楚就著急動手實現了?...透過一次次的參賽和總結,我們終究能鍛煉出一個良好的應試狀態,並且對常見知識點信手拈來。
周賽難度周賽一共有四道題目,分別對應的難度是:
基本不涉及任何演算法,只是考察程式碼實現,幾乎無難度。考察一些簡單的程式碼知識。考察某類演算法,題目一般較難。考察某類演算法,或者綜合考察一系列演算法。演算法可能 超綱,題目難度浮動很大,最簡單的可能是 medium 略高難度,最難的可能遠超普通 hard 題目。一般如果大家完成了資料結構部分的訓練,對於周賽的前兩題壓力應該是不大的,某些情況下第三題也可以輕鬆地解出來。如果是系統地完成了演算法學習的同學,順利的情況能夠完成前三道題目。對於第四道題目,在有餘力的情況下也可以儘量嘗試,某些題目簡單的場次還是能夠做出來的。
小知識:第四題之所以難度浮動會大,是因為力扣實際上沒有 hard 以上的難度評級,這就導致了力扣的題目只要比 medium 難都會歸為 hard 難度。因此 hard 難度反而是最難區分的難度範疇。包括大家刷題時,可能覺得有的 hard 很簡單, 有的 hard 很困難,也是這個原因。
周賽名次如果能夠順利透過前兩題,基本能穩定拿到 1000 ~ 2000 名左右的名次。如果進一步能透過三道題目,排名會來到 100 ~ 1000。發現了什麼沒有?前三題都能有 1000 人透過,但是透過四題的人一場可能也就一兩百人,這也是為什麼說不用對第四題進行苛求。至於前 100 名的位置,基本都是透過四題的人了,這時比較的就是透過時間和罰時,甚至於前 10 的人很多能在 20 分鐘以內完成四道題目,這對於我們可能是一個比較長期的目標了。
而短期的話,可行的路線是這樣的:
嘗試保證透過兩道題目,排名 2000 以內。快速正確透過兩道題目,透過第三題,排名 500 - 1500。死磕前三題,保證準確率(免罰時),保證實現速度,排名 100 - 200。前三題穩定透過,嘗試第四題,排名偶爾能進前 100。在經過一段時間的訓練,完成 1 和 2 壓力還是不大的,但是到了 3 和 4 就需要付出更多的努力了。我們並不需要過分關注別人的成績,例如前 10 名的人絕大部分都是 OI 或者 ACM 選手,很多人從高中起就開始刷題和訓練了。只是針對面試和基礎的話,我們並不需要到那種水平,多餘的時間用來增加其他方面的專業技能會更有價值。
周賽小技巧最後我們再針對一些常見技巧進行一些說明:
1. 關於程式的執行速度:演算法執行的速度由兩部分組成,一部分是演算法複雜度,比如 O(n),另外部分是常量複雜度,比如 O(n) 也分到底跑了幾遍 。在解題過程中,如果你發現你的程式執行速度慢,要注意區分是由於複雜度高,還是常數大。對於面試來說,只要你的演算法複雜度是最優的,一般不會太介意常數大的情況,除非題目特殊要求。因此也不需要去針對別人 2ms,你 5ms,一定要最佳化那 3ms。
2. 關於空間複雜度,除非題目有特殊要求,一般不用太過苛求,不過你也需要知道一些基本的壓縮空間的方法,例如如何用 O(m) 的空間寫 01 揹包。
3. 關於語言選擇,不同的語言本身執行速度也是不一樣的,都是正常的。因為很多題目並沒有因為是 Python 所以合理地延長時限,導致有些題目正確的時間複雜度仍然有可能會超時,用 Python 需要注意。
4. 關於最優時間複雜度,有的題目可能不確定最優的時間複雜度是什麼,比如看上去有一個 O(n^2)的解,但是仔細最佳化可以有一個 O(n) 的解,但是不容易想到。在不能確定演算法最優複雜度的時候,我們不妨看看資料範圍。比如 n 是 10^6 ,那麼 O(n^2) 的時間複雜度就是 O(10^12)。經驗上,我們可以認為計算機跑 10^8 複雜度的操作需要一秒。 那麼 10^12 就需要 1000 秒,顯然系統是不會給十幾分鍾用於單次評測的。那麼反過來可以推斷這個題的最優解是 O(n) 或者O(nlog(n)) 。其他情況,如果 n 是 1000,那麼就有可能是 O(n^2) ,如果 n 只有 20 以內,那麼就考慮 O(2^n) 了。
本文內容選自:LeetBook《高頻演算法實戰》
內容簡介:這門課程將幫助大家梳理面試中必須掌握的基礎知識點,學習常用解題技巧,真正動手去 AC 每一個知識點。課程中還會討論很多經典的演算法題目,包括力扣題和小函老師親自設計的題目,並對這些問題進行擴充套件發散,追蹤知識點的原理,幫助大家知其然且知其所以然,而不只是記住解法。