打破內卷,還是得靠刷題!

不知道你們一開始刷算法題的時候是否有過如下的困惑。

  • 1、題目很長,半天看不明白是什麼意思
  • 2、明明看懂了題目,但寫出的代碼卻提交不通過
  • 3、代碼寫到一半發現不知道怎麼往下寫了
  • 4、別人的題解一看就懂,但自己想破腦袋都想不出要用這種方法
  • 5、即使刷了兩百多道題目,面試的時候一緊張還是會頭腦空白髮慌

你問我爲啥知道這些?因爲我都經歷過。

我們都是從應試教育中廝殺出來的,所以對刷題應該是挺熟悉的,缺的只是方法,人人都可以是小鎮做題家。

方法是什麼呢?

可以歸納爲兩個,一個是戰略,一個是戰術,猶如行軍打仗,兩手都要抓。

在戰略上,我們需要做到的是藐視算法題。

在戰術上,我們需要做到的是重視刷算法題。

一、戰略上藐視算法題

在戰略上藐視算法題的目的是爲了在心理層面上克服恐懼,事實上,不僅僅是算法題,諸如學習計算機基礎、計算機網絡、編譯原理等程序員必備的知識時,有這個心態可以學起來事半功倍。

我親身經歷過這樣的改變。

作爲一個轉行程序員,在轉行學習編程的那段日子,沒有人告訴我說要去學數據結構,也沒有人告訴我說要去刷 LeetCode,都是靠自己一個人摸索,絕大部分的時間都花在具體項目上,誤認爲自己和那些已經工作的程序員的區別在於有沒有做過項目。

這就是科班出身和非科班出身的學生最大的區別,科班出身的學生知道去學什麼,知道大學期間安排的每一門課程是幹什麼的,知道要先去做哪些小項目來循序漸進的編碼練習,知道知識點在工作中能起到什麼樣的具體作用,非科班出身的程序員感覺計算機相關的知識點簡直是一團亂麻。

這種情況導致我去找工作參加面試的時候,很多基礎面都通過不了,最後僥倖進入一家要求不太高的創業公司,薪資不高,爲了進大廠必須完善算法和計算機的知識。

因爲未知,所以恐懼,恐懼導致盲目的崇拜,我認爲那些科班出身的程序員太牛逼了,居然可以掌握那麼多繁雜的計算機知識;那些寫源碼分析的程序員都是大神;那些寫算法題解的程序員都是高手。

恐懼把小的問題放大,比如學到單調棧、雙向鏈表、記憶化的內容,一看到題目要用到這些概念便覺得代碼很難寫,索性那些內容就不看,所以學了大半年還在原地踏步,還在原來的公司拿着微薄的工資做着 CURD。

幾個月後,創業公司不行了,受迫於重新找工作的壓力,只好咬着牙再去啃,再去刷題。

而當我開始寫題解、做動畫的時候,我就發現算法題也就那樣,缺的只是時間去不斷的重複練習。

單調棧無非就是在棧的概念基礎上增加了排序,記憶化也就是增加一個數組用於存儲,動態規劃在面試和實際工作中用到的只需要掌握百分之五就行。

抱着編程技術也就那樣的心態,學習了爬蟲以及 Vue。

“自大”的認爲爬蟲能有多難,基本步驟無非以下幾步:

  • 1、找到需要爬取內容的網頁URL
  • 2、打開該網頁的檢查頁面
  • 3、在 HTML 代碼中找到你要提取的數據
  • 4、寫 Python 代碼進行網頁請求、解析
  • 5、存儲數據

下載安裝 Pycharm,安裝 Scrapy,根據步驟輸入 URL 和數據格式,在完全不懂分佈式、ip代理、js加密、模擬登陸、MongoDB的前提下,順利拿到了自己想要的數據。

無論是分佈式還是ip代理,爬蟲的每個知識點深挖下去都大有文章,我所學習到的爬蟲知識只是冰山一角,但這並不妨礙我們可以從戰略上去蔑視編程,編程的很多內容沒有那麼高不可攀,缺的只是時間去學習,時間恰恰是我們可以去支配的。

二、戰術上重視刷題

戰術,分爲道與術。

回顧一下我們以前學數學的過程,會發現,數學題有千千萬,最後在腦海中記住的並非是這道題的具體寫法,而是解這道題的思路。

算法刷題同樣如此,很難做到讓你把做過的題目代碼都背下來,然後在面試的時候一五一十的寫出來,但是你可能知道這道題的思路,用什麼樣的數據結構和什麼樣的算法思想,知道可以用這樣那樣的方法做出來,差的就是細節。

也就是說,刷題和應試教育中的學習是一樣的,都需要先經過大量刻意的重複練習,見多識廣,才能在面試時做到遊刃有餘。

說白了,就是要多刷纔行。

這裏的多刷題,不是指多瞎刷題,而是有方法的去刷,有目的的練習,而一個合理的練習方式,比練習的時間長短,更爲重要。

如何做到有目的的進行練習,大概可以分爲以下五個步驟:

1、找到具有定義明確的具體特定目標 2、具有專注練習的狀態 3、找到導師模仿練習 4、走出舒適圈,突破自我 5、強化前行的理由

1、找到具有定義明確的具體特定目標

目標必須是十分具體的,可以逐個解決,把目標進行分類並制定一個可實施的計劃。

目標是什麼?

通過算法面試不是目標,而是一個結果,我們的目標是怎麼樣合理的刷完算法面試需要的那些題目,推薦的做法是按照標籤來刷,難度上循序漸進,即把多種同類型的題先放在一起來做,比如一個時間段,只刷鏈表題,待刷得差不多的時候,接下來再刷二叉樹的題。

由於不斷的刷同個類型的題目,相當於在不斷的重複練習,可以不斷地加深自己對某個數據結構的理解,刷到後面可能發現這類題目都是有固定的套路,甚至一部分代碼都是一模一樣的。

這種刷法不僅在大方向上找到具有定義明確的具體特定目標,即合理的刷完算法面試需要的那些題目,與此同時,當刷同類型的題目出現困惑時,也能有目的性的去搜索相關的特定資料。

2、具有專注練習的狀態

不建議在一開始刷題就去搜索一些模板來背,然後在解題的時候套模板,這樣的刷題只是重複而不是練習,收穫的只是經歷而不是經驗,背的再熟練,平時寫的多塊,沒有自己的一個完整思考過程,在面試時很容易卡殼。

在刷題的過程中,爭取做到三件事:

1、當寫出 AC 的代碼時,思考爲什麼自己可以做到

2、當寫出 AC 的代碼時,思考能不能優化一些

3、是否用到了題目給出的所有條件

很多題目都是由相似的題目改編而來的,增刪一些條件題目的難度就會發生巨大的提升,基於這三個思考,每道題目都去多想一步,一步一步再一步,不同維度不同姿勢都嘗試一下,不要滿足於一種解法,各種解法都寫一寫,爭取做到 beat 100%,把每個題目都做乾淨,徹底攻克一道題。

3、找到導師模仿練習

搜索任何一道算法題,在網上都能發現不少文章,不過很多文章都是隻提供解題代碼或者加上一些簡單的文字說明,爲什麼要這麼寫以及是怎麼樣想到這些方法的很少有文章會涉及到,這些人是高手,卻不是導師。

高手和導師最大的區別在於,很多高手未必可以總結出自己的方法論,他們真的很牛逼但核心內容卻只可意會不可言傳,而導師一定有一套可以複製的方法論,他或許不一定是最牛逼的,但卻是最適合模仿學習的。

目前 LeetCode 的題解區有不少大神寫了不少細緻的題解,找幾個你看的順眼的,模仿他們的思路去思考問題。

然後悄咪咪的吹一下自己,我利用動畫的形式講解算法,寫了幾百篇文章了,期間有不少人也在模仿我的風格去寫作,取得了不錯的效果,我最近把精力花在自己的個人網站 AlgoMooc 上,立志於更加細緻的講解 LeetCode,如果你找不到合適的導師,不妨訪問 https://www.algomooc.com 來看看我的文章,我爭取每道題目都錄製視頻,用五分鐘講清楚。

4、走出舒適圈,突破自我

當我們跌跌撞撞的刷了一些題目時,實際上,刷題已經變成了我們的舒適圈,在這個圈子中,你已經可以熟練的掌握了一些知識,如果我們想讓練習取得成績,我們得逼着自己走出舒適圈,最好的方法是自己去寫題解,寫一篇新手也能看懂的題解。

也就是熟知的費曼學習法

什麼是費曼學習法呢?

簡單來說就是以教促學,每當你認爲學會或者掌握一個知識後,去給別人講明白,通過這種方式對自己做一個檢驗,突破自我。

李笑來曾經分享過一個觀點,他說教育主要分爲 3 個環節——

  • 1、 教:我們最常做的讀書、學習、聽課等

  • 2、 練:就是練習,大量練習,重複練習

  • 3、 教練:在練習過程中遇到問題,教練幫忙指出來,然後繼續練。

1、2 不斷循環,直到把知識、技能練熟,能用到實踐中,幫自己做成一些事情,創造價值。

以此作爲參考,刷題也是可以分爲 3 個環節---

  • 1、學:閱讀別人的提交
  • 2、練:就是練習,模仿別人的思路來練習
  • 3、教:就是教練,通過寫題解的形式給別人講明白一道題目

1、2 兩點屬於被動學習,吸收效率在 10% 至 30% 之間,而 3 屬於主動學習,也就是費曼學習,吸收效率高達 90% 。

也就是說,我們在刷題的過程中,爲了提高學習效率,可以主動的去寫技術博客分享,注意是寫技術博客而非技術筆記,筆記是給自己看的,博客是給別人看的,在這個過程中,表面上你是在教會別人,事實上你通過教會別人的方式來逼自己查缺補漏,你可能以爲你懂了,結果發現無法表達出來,事實上還是沒有理解透徹;你以爲你講明白了,別人一問,發現還是有遺漏點。

5、強化前行的理由

當初你覺得進行刷題提升自己的時候,什麼是你的動力?

這個問題最好在一開始的時候就想清楚,並記錄下來。

在《思維的囚徒》一書中,提及到一個原則,叫:自由地選擇你的態度 —— 人無論在什麼情況下,都可以自由選擇自己的態度。事實上,任何一件事情,我們都能找到它的意義,它能幫助自己變得更好的角度。

刷題這個過程必然是有難度的,會給自己很大的壓力,所以一開始先把你認爲刷題後能帶來的積極結果寫下來,越多越好,不管現實與否,每當你想要放棄的時候,多想想這些積極的結果,想想熬過這個痛苦的過程能提升多大的改變。

是從宏觀角度來思考刷題,那麼則是在微觀的角度來看待每一道題目。

在具體做題的時候,可以採用以下三個步驟來進行。

  • 1、看懂題目

  • 2、分析解法

  • 3、代碼實現

1、看懂題目

首先就是明確題目要我們解決的是什麼問題?提供了哪些參考示例?是否提供了需要使用的數據結構和算法?時間複雜度或者空間複雜度有沒有要求?提示的範圍有沒有比較特別的數?邊界情況是否需要特殊處理?

怎麼樣去看懂題目呢?

給你個公式步驟進行參考,即 四步分析法 !

  • 模擬:模擬題目的運行。
  • 規律:嘗試總結出題目的一般規律和特點。
  • 匹配:找到符合這些特點的數據結構與算法。
  • 邊界:考慮特殊情況。

結合一道具體的算法題來說明整個過程,算法題來源於劍指 offer 上的例題:矩陣中的路徑。

1、模擬

首先看一下矩陣的初始狀態

我們需要在這個矩陣中尋找目標字符串 bfce,第一步要做的就是先匹配上目標字符串的第一個元素 b,我們從矩陣的第一行第一列的元素開始匹配,找到了 a 。

目標字符串爲 bfce,此時查找第一個元素爲 a ,與目標字符串的第一個元素 b 不相同,需要在四個方向搜索,看看能不能找到符合要求的元素,我們按照上左下右的順序進行遍歷尋找。

  • 上:越界了
  • 左:越界了
  • 下:是 s ,與目標元素  b 不相同
  • 右:是 b,符合要求,依葫蘆畫瓢找第二個元素
  • 上:越界了
  • 左:是 a,與目標元素  f 不相同
  • 下:是 f,與目標元素  f 相同,符合要求,依葫蘆畫瓢找第三個元素
  • 上:根據題目要求不需要考慮
  • 左:是 s,與目標元素  c 不相同
  • 下:是 d,與目標元素  c 不相同
  • 右:是 c,與目標元素  c 相同,符合要求,依葫蘆畫瓢找第四個元素
  • 上:是 c,與目標元素  e 不相同
  • 左:根據題目要求不需要考慮
  • 下:是 e,與目標元素  e 相同,符合要求,尋找結束,匹配成功,返回 true

2、規律

  • 1、在搜索過程中,如果當前元素與目標元素 不匹配,則回退到之前的節點再搜索
  • 2、在搜索過程中,如果當前元素與目標元素 相匹配,則按照 上左下右的方向進行再次搜索匹配剩下的元素
  • 3、在搜索過程中,搜索 當前元素上左下右方向的元素時,會出現 重複訪問之前元素的情況,比如搜索匹配成功的第三個元素  c 的四個方向時,會重複訪問一下  f

爲了保證不重複訪問節點,可以將這條路徑上已經訪問過的節點,修改爲不在 word 當中的一個字符,保證以後再次訪問時不會重複訪問,這裏我們將其修改爲特殊字符  # 。

修改完後會出現一種情況,當前的節點元素與目標元素相匹配,但是在它的四個方向的節點中都找不到可以匹配到目標下一元素的節點。

比如此時當前元素 c 與目標元素 c 相匹配,但是目標下一元素爲 x,而在當前元素的四個方向上都找不到 x ,需要把這個點回退,根據之前的操作,當前的節點被修改爲了 #,所以爲了能夠回退成功,再回退操作時需要重新將 # 修改回原來的元素。

3、匹配

本題提供了一個矩陣,矩陣是一個二維數組,需要我們在二維數組中進行搜索,爲了能夠覆蓋所有的情況,必然要使用兩個嵌套的循環

在搜索過程中,當遇到匹配成功的元素,搜索其下一元素的操作與當前的操作一致,即可以使用遞歸

  • 遞歸參數:當前元素在矩陣 board 中的行列索引 i 和 j ,當前目標字符在word 中的索引 k

  • 終止條件

    • 返回 false

      (1) 行或列索引越界

      (2) 當前矩陣元素與目標字符不同

      (3) 當前矩陣元素已訪問過

    • 返回 true: k = len(word) - 1 ,即字符串 word 已全部匹配。

  • 遞推工作

    • 標記當前矩陣元素:將  board[ i ] [ j ] 修改爲特殊字符  # ,代表此元素已訪問過,防止之後搜索時重複訪問。
    • 搜索下一節點:朝當前元素的  上、左、下、右 四個方向開啓下層遞歸。
    • 回退時還原當前矩陣元素:將  board[ i ] [ j ] 元素還原至初始值,即 word[k] 。
  • 返回值: 返回布爾量 res ,代表是否搜索到目標字符串。

4、邊界

  • 1、行越界
  • 2、列越界
  • 3、矩陣元素已訪問過

2、分析解法

在看懂題目的前提下,分析解法就輕鬆多了, 在腦海中我們已經大概知道了題目想要考察的方向,接下來我們需要思考的是題目的邏輯是怎麼樣的,不需要考慮代碼層面。

需要注意的是,在第一遍或者第二遍解題時,不要過分追求所謂奇淫技巧的解法,很多同學錯誤的認爲了奇淫技巧等於水平高超,我之前也出現過這個誤解,很多 LeetCode 上的數學題都能一行或兩行代碼就 AC,每次自己寫大半天發現答案竟然如此簡單很是受挫。

後來發現,這些奇淫技巧並不能提高自己的水平,除了發在評論區能引來別人一句臥槽的驚訝,從而帶來一點內心虛榮心的滿足以外,其餘的用處不大。

當然,等你刷個兩三百到題目再回過頭來重新思考,你會發現那些奇淫技巧的方法是如此的美妙。

3、代碼實現

看懂了題目,有了思路,其實這道題的 90% 你已經解決了,把它實現出來按理來說就是自然而然的事兒了。

有時,將一個思路轉換成算法是很容易且自然的;但有時,有些思路轉換成代碼,是很有難度的事情,這就是你寫代碼的能力問題,其實就是練少了。

最後呢,說到底,刷題還是需要題海戰術

本文分享自微信公衆號 - 五分鐘學算法(CXYxiaowu)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章