天天看點

學妹:大學四年以算法為重還是技術為重?

經常有學妹問我(其實學弟也愛問):

大學應該更偏向技術還是算法和資料結構這類。

大家都是成年人了,這還用選嗎?

當然是兩者都要重點啃下來呀,算法和技術相輔相成的,一定不要有二選一的想法!

算法和資料結構可以說是技術(包括mysql、java、redis、作業系統這些)的基石:

學妹:大學四年以算法為重還是技術為重?

我當時大一也是覺得資料結構沒啥用,哪有學個 js、css 寫個漂亮的網頁炫酷?

什麼算法,明明有 qsort 還要學快排、堆排?

這玩意有 qsort 快嗎?

我直接一行就排好序了,你還要寫十幾行,真菜呀!

那時候以為的技術就是使用各種元件、調api,比如 map:

學妹:大學四年以算法為重還是技術為重?

但是越學到後面心裡越沒底,因為這些東西對自己都是黑盒子。

是以如果資料結構與算法掌握不好,那麼這些 api 對于我們就是一堆的黑黑子,連什麼時候用 map(紅黑樹實作)、什麼時候用 hashmap 都分不清。

redis 這種元件,難道隻需要了解如何get、set 就是算是掌握了嗎?

那肯定不行,實際上想要要用得好,得要了解 redis 底層的那些資料結構,比如簡單動态字元串(sds、連結清單、字典、跳躍表、整數集合、壓縮清單,才能選擇适當的存儲結構。

如果要問我大學什麼最後悔?那肯定是沒有從大一就開始好好學算法,去打 acm。

現在還在大一、大二的同學還不抓緊機會,别給自己留下遺憾。當然,不打 acm,我們也是能夠學好資料結構和算法的。

資料結構和算法你能在任何計算機領域裡看到,比如在編譯原理中寄存器的配置設定會用到貪心,死代碼檢測與消除會用到圖論裡不可達的知識;作業系統程序、線程排程會用到多級隊列和排程算法;組成原理中 cache 的替換會用到 lru、fifo 等算法;開發必備的資料庫也離不開b+樹、lsm 等資料結構和查找算法。

很多時候我們需要的算法都被封裝到程式設計語言的基礎庫裡了,以至于很多同學會覺得算法離我們太遠,其實不是的。

是以學習算法有助于我們根據應用場景選擇最合适的資料結構。

日常開發中也一定離不開算法,比如小北最近工作中涉及的某種嵌套 tlv(tag-length-value)結構編碼的解析,就需要用到遞歸、多叉樹等知識。如果不學習算法,那麼程式中隻能見到大量的 if/else、while/for。。。

可以說不學算法的工程師一定不是一個優秀的工程師。

再來說作業系統、編譯原理,這些裡面也是蘊含着各種資料結構與算法的,就拿編譯原理來說。

當你學完有限狀态機以後,你會發現以前覺得很牛逼正規表達式似乎自己也能用 dfa、nfa 實作一下了。狀态機的思想在程式設計中很多地方都用得上。

比如解析 http 協定,如果沒學過狀态機思想,你可能會一行行的 if/else 去做解析,這裡最麻煩的地方在于,if/else 需要提前将 http 頭部字段都接收到再來判斷,而我們知道 http 基于 tcp,而 tcp 是流式傳輸,是以你很有可能是幾個字元一組組接收到的,這個時候用 if/else 寫出來就很難看了。

而用狀态機編寫起來代碼就會非常優雅。狀态的轉移是由規則驅動的,接收到一個字元就判斷一個,非常的友善。

繼續學完文法分析,你會掌握遞歸下降分析這樣非常重要的思想,你可以使用遞歸下降快速的實作四則運算電腦。

如果不用遞歸下降你可能需要先中綴表達式轉字尾,然後求值,這是我們大一資料結構課寫的,當時用棧寫的,有點麻煩。後來學完編譯原理,又用遞歸下降重寫了一遍,區區幾十行代碼遍搞定。

還有一類場景在實際開發中的用的很多,比如淘寶、京東這樣的電商,它們的營銷規則有很多,比如滿減、直減、跨店等等,這樣的規則是不可能寫死在代碼裡的。

那是怎麼做的呢?

一般會實作一個配置系統,并設計一個dsl(領域特定語言)來表達這些規則,将規則直接配置到系統中,這樣可以非常友善的修改,那麼如何在代碼裡去解析 dsl 定義的規則呢?這就需要為 dsl 寫一個文法解析器,這裡就會用到文法分析的方法。

dsl(domain specific language),它是一種用于某個特定領域的程式設計語言。這種特定于某個領域是相對于 c、c++、python 這種通用語言而言的,通用語言可以在各個領域使用,我們熟悉的大多數程式設計語言都是通用語言,它們都是圖靈完備的。

像我們平常經常使用的 json、sql、html 這些都算是一種 dsl,你甚至可以嘗試用遞歸下降去寫一個 json、xml 解析器,這比寫電商網站更有價值的。

繼續往下學你會了解到抽象文法樹 ast 如何生成、如何轉化為中間代碼、如何對中間代碼優化、最終又是怎麼生成機器指令的。

你會看到貪心算法在寄存器配置設定中的應用,也會看到圖論中的可達性分析又是如何實作死代碼消除。

是以無論是作業系統、計算機網絡、編譯原理這些基礎cs課程,還是mysql、redis這些中間件,都是建構在各種精妙的資料結構與算法之上的,資料結構與算法必學,一定要重視!

如果你有 acm 獲獎經曆,那 bat 是很容易進的,但是也一定要掌握基本的cs基礎課程知識,不能隻重算法不重基礎。

國外可能把題刷好就能拿到offer,但是國内不懂 os、網絡這些基礎和一些語言八股文也是很難的!

很多大一大二的同學其實是不太清楚到底該計算機專業該如何自學,在這分享下我的學習路線吧:

我大學專業學計算機的,對 cs 大學課程還算了解,也經常了解學習國外 cs 課程。

cs 專業差別于其它專業很大特點就是:

工作後的内容是和專業所學的内容強相關的

比如你學了資料結構、編譯原理、作業系統、計算機網絡,如果你從事的是研發崗,那一定離不開這些知識。

主要靠自學

不管是科班還是非科班,想要快速持續的提高技術水準,就得靠自己去鑽,尤其離不開自學。

知乎上其實很多問科班和非科班的差别在哪,其實我一直想說,你給自己充足時間去把科班的内容學習一遍,到底還能差在哪呢?

可能唯一差别就是少了一個 計算機學士學位。

也有人把這種自學出家的叫做民科,當然沒有任何的諷刺意思哈。

最簡單的方式就是參考 cs 科班同學的課程,比如下面這個:

學妹:大學四年以算法為重還是技術為重?

img

其實看着很多,概況起來就是(下面隻涉及cs專業課):

計算機導論 + 一門程式設計入門語言

算法與資料結構

作業系統

計算機網絡

資料庫系統

特定領域,如:計算機圖形學、資訊安全、system方向、分布式

學習的途徑就是:

多看國外/國外的 cs 名校的一些開放課程 + 看經典的書 + 多寫代碼!!!

畢竟現在mooc、udemy、b站上學習的資源都是很豐富的。

唯一要做的就是篩選一些比較好的課程進行學習,在這裡我主要推薦一些國外的計算機課程,他們很明顯的一個特征就是注重實踐。

一門課,除了理論以外,還會有配套的 lab、assignment,而且這些老師設計 lab 都很用心的,看視訊/書 + 做 lab,這應該算計算機科班同學一個比較好的學習方式了,有理論也有實踐。

下面開始上幹貨:

一、計算機導論

首先建議從計算機導論課程開始,推薦下面這些課程:

harvard的cs50  cs50: introduction to computer science

berkeley的cs61a  cs 61a: structure and interpretation of computer programs

mit的6.001  mit-6.001

随後建議學習一門語言,可以是c、java、或python,我推薦 c語言(當然,也可以是python!這不是重點,重點是要多去寫,入門時提高對程式設計的興趣)。

提到c語言,我這裡推薦國内浙大翁凱老師的課,看過的都說好,分為兩門:

第一門是面向聯考結束想提前自學一點程式設計的,叫大學先修課:c語言程式設計cap-大學先修課

雖然叫先修課,但是覆寫了c語言的主要知識點,也适合大一新生~

第二門是c語言程式設計進階:c語言程式設計進階

會帶你用c語言完成一些有趣的項目,比如一些圖形界面小遊戲,先修課學習c語言文法基礎,進階課帶你項目實操,搭配使用,你就是同學中的大神!

有了語言基礎之後建議學資料結構與算法:

資料結構推薦:

stanford cs106系列

cs106a: programming methodologies

算法推薦:

6.046(進階)  design and analysis of algorithms - mit

mit的6.006  introduction to algorithms

coursera上的princeton課程

berkeley的cs61a 和 cs61b

學習完經典的資料結構和算法之後就可以去刷題了。

cmu的15-213

berkeley的cs162,

這兩個都是有視訊有lab的好課

還有一個非常經典的 mit 6.828,附帶一個xv6 lab

課程:6.828: operating system engineering

mit的6.004,

berkeley的cs61c

stanford的cs144,lab 很有意思

一個原則,來自翁凱老師:

學計算機一定要有一個非常強大的心理狀态,計算機的所有東西都是人做出來的,别人能想的出來,我也一定能想得出來,在計算機的世界裡沒有任何的黑魔法,所有的東西隻不過是我現在不知道而已,總有一天我會把所有的細節、所有的内部的東西全搞明白的

建立抽象層,我自己的感悟

計算機裡,幾乎都是人造的概念,大部分的東西,隻要你一直深挖下去,幾乎都可以搞明白。

但是要注意時間成本,軟體行業已經不是一般的複雜和巨大,任何一個領域的知識的複雜性都足夠耗費掉我們一生的時間,是以一定要抓住主線,對于技術和知識,要學通用的、流行的,可以嘗試面向面試學習。

“打破砂鍋問到底”式的學習雖然精神可敬,但成本效益并不劃算。

一定要學會在适當的層次上抽象出一層,并且認可這一層提供的接口,不去深究内部實作,了解原理即可,不必深究内部實作。

比如學習 http,那麼就先認可 tcp 提供的穩定可靠傳輸,而不繼續深挖 tcp 的内容,等到學習傳輸層的時候再去深入挖掘 tcp 具體實作。

也就是我們常說的面向接口/抽象程式設計。

視訊為主,看書為輔

新手,一定不要一直看書,保持看書的時間不超過 50%,按照下面的流程:

看書學習基本的理論

程式設計練習、實踐

有了新領悟,繼續看書

如此反複的循環。