嚴格來講,本文題目應該是個人數據結構和算法學習之路,但這個寫法實在太繞口——何況CS中的算法每每暗指數據結構和算法(例如算法導論指的其實是數據結構和算法導論),因此我認爲本文題目是合理的。html
若是你使用的是手機或平板設備,那麼請點擊下面的連接以得到更好的閱讀效果:程序員
http://zh.lucida.me/blog/on-learning-algorithms/面試
原文做者:Lucida算法
第一次接觸數據結構是在大二下學期的數據結構課程。然而這門課程並無讓我入門——當時本身正忙於倒賣各類MP3和耳機,對於這些課程根本就不屑一顧——反正最後考試劃個重點也能過,因而這門整個計算機專業本科最重要的課程就被傻逼的我直接忽略過去了。編程
直到大三我才反應過來之後還要找工做——並且大二的折騰證實了我並無什麼商業才能,之後仍是得靠碼代碼混飯吃,我當時驚恐的發現本身對編程序幾乎一無所知,因而我給本身制訂了一個相似於建國初期五年計劃的讀書成長計劃,其中包括C語言基礎、數據結構以及計算機網絡等方面的書籍。網絡
讀書計劃的第一步是選擇書籍,我曾向當時我以爲很牛的"學長"和"大神"請教應該讀哪些算法書籍,"學長"們均推薦算法導論,還有幾個"大神"推薦計算機程序設計藝術(如今我疑心他們是否翻過這些書),草草的翻了下這兩本書發現實在看不懂,但幸運的是我在無心中發現了豆瓣這個神奇的網站,裏面有不少質量不錯的書評,因而我就把評價很高並且看上去不那麼嚇人的計算機書籍都買了下來——事實證實豆瓣要比這些"學長"或是"大神"靠譜的多得多。數據結構
數據結構與算法分析——C語言描述是我學習數據結構的第一本書:當時有不少地方看不懂,因而作記號反覆看;代碼看不明白,因而抄到本子上反覆研讀;一些算法想不通,就把它全部的中間狀態全畫出來而後反覆推演。事實證實儘管這種學習方法看起來傻逼併且效率很低,但對於當時一樣傻逼的我卻效果不錯——傻人用傻辦法嘛,並且這本書的課後題大多都是經典的面試題目,以致於往後我看到編程之美的第一反應就是這貨的題目不全是抄別人的麼。架構
至今記得,這本書爲了說明算法是多麼重要,在開篇就拿最大子序列和做爲例子,一路把複雜度從O(N^3)殺到O(N^2)再到O(NlgN)最後到O(N),當時心裏真的是景仰之情如滔滔江水連綿不絕,尼瑪爲什麼能夠這麼屌。app
此外,我當時還把這本書裏圖算法以前的數據結構全手打了一遍,後來找實習還頗爲自得的把這件事放到簡歷裏,如今想一想真是傻逼無極限。數據結構和算法
憑藉這個讀書成長計劃中學到的知識,我總算比較順利的找到了一份實習工做,這是後話。
個人實習並無用到什麼算法(如今看來就是不停的堆砌已有的API,編寫一堆本身都不知道對不對的代碼而已),在發現身邊的人工做了幾年卻還在和我作一樣的事情以後,我開始愈來愈不安。儘管當時我對本身沒什麼規劃,但我清楚這絕壁不是我想作的工做。
在這個搖擺不定的時刻,微軟的夢工場成了壓倒駱駝的最後一支稻草,這本書對微軟亞洲研究院的描寫讓我下定了"找工做就要這樣的公司"的決心,然而我又悲觀的發現不管是以我當時的能力仍是文憑,都沒法達到微軟亞研院的要求,矛盾之下,我完全推翻了本身"畢業就工做"的想法,辭掉實習,準備考研。
考研的細節無需贅述,但至今仍清楚的記得本身在複試時驚奇且激動的發現北航宿舍對面就是微軟西格瑪大廈,那種離理想又進了一步的感受簡直爽到爆。
個人研究生生涯絕對是一個反面典型——翹課,實習,寫水論文,作水研究,但有一點我頗爲自得——從頭至尾認真聽了韓軍教授的算法設計與分析課程。
韓軍給我印象最深的有兩點:課堂休息時跑到外面和幾個學生借火抽菸;講解算法時的犀利和絕不含糊。
儘管韓軍歷來沒有主動說起,但我敢確定算法設計與分析基礎就是他算法課程事實上的(de-facto)教材,由於他的課程結構幾乎和這本書的組織結構如出一轍。
若是數據結構與算法分析——C語言描述是個人數據結構啓蒙,那麼韓軍的課程和算法設計與分析基礎就是個人算法啓蒙,結合課程和書籍,我一一理解並掌握了複雜度分析、分治、減治、變治、動態規劃和回溯這些簡單但強大的算法工具。
算法引論是我這時無心中讀到的另外一本算法書,和普通的算法書不一樣,這本書從創造性的角度出發——若是說算法導論講的是有哪些算法,那麼算法引論講的就是如何創造算法。結合前面的算法設計與分析基礎,這本書把我能解決的算法問題數量擴大了一個數量級。
以後,在機緣巧合下,我進入微軟亞洲工程院實習,離理想又近了一步,自我感受無限牛逼。
在微軟工程院的實習是我研究生階段的一個很是很是很是重要的轉折點:
這裏就不說1和3了(和本文題目不搭邊),重點說說2。
因爲當時組內沒有特別多的項目,我負責的那一小塊又提早搞定了,mentor便很慷慨的扔給我一個Kinect和一部Windows Phone讓我研究,研究嘛,天然就沒有什麼deadline,因而我就很雞賊的把時間三七開:七分倒騰Windows Phone,三分看書&經典論文。
然而一件事打斷了這段安逸的生活——
基友在人人發百度實習內推貼,當時自我感受牛逼閃閃放光芒,因而就抱着看看國內IT環境+虐虐面試官的變態心理投了簡歷,結果在第一面就本身的師兄爆出翔:他讓我寫一個stof(字符串轉浮點數),我磨磨唧唧半天也沒寫出完整實現,以後回到宿舍趕快寫了一個版本發到師兄的郵箱,結果對方壓根沒鳥我。
這件事對我產生了很大的震動——
痛定思痛,我開始了第二個"五年計劃",三七開的時間分配變成了七三開:七分看書,三分WP。而這一階段的重點從原理(Principle)變成了實現(Implementation)——Talk is cheap, show me the code.
因爲一直以爲名字裏帶"Elements of"的都是酷炫叼炸天的書,因此我幾乎是絕不猶豫的買了這本Elements of Programming,事實上這本書裏的代碼(或者說STL的代碼)確實是:快,狠,準,古龍高手三要素全齊。
百度面試被爆出翔的經歷讓我意識到另外一個問題,絕大多數公司面試時都須要在紙上寫C代碼,而我本身卻不多用C(多數狀況用C#),考慮到本身還沒牛逼到能讓公司改變面試流程的地步,我須要提高本身編寫C代碼的能力(哪怕只是爲了面試)。一頓Google以後,我鎖定了C Interfaces and Implementation——另外一本關於如何寫出狂炫酷帥叼炸天的C代碼的奇書,這裏套用下Amazon的評論:Probably the best advanced C book in existance。
嚴格來講上面兩本書都不是傳統的算法書,由於它們側重的都不是算法,而是經典算法的具體實現(Implementation),然而這正是我所須要的:由於算法的原理我能說明白,但要給出優雅正確簡練的實現我就傻逼了,哪怕是stof這種簡單到爆的"算法"。
依然是之前的傻逼學習方法:反覆研讀+一遍又一遍的把代碼抄寫到本子上,艱難的完成了這兩本書後,又讀了至關數量的編程實踐(Programming Practice)書籍,自我感受編程能力又大幅提高,此外得到新技能——紙上編碼。這也成爲了我以後找工做面試的三板斧之一。
說老實話,自從本科實習以後,我就一直以爲算法除了面試時能用用,其它基本用不上,甚至還寫了一篇當時頗爲自得如今讀起來極爲傻逼的文章來黑那些動不動就"基礎"或"內功"的所謂"大牛"們,這裏摘取一段如今看起來很傻逼但當時卻以爲是真理的文字:
因此那些動則就扯什麼算法啊基礎啊內功啊所謂的大牛們,請閉上你的嘴,條條大道通羅馬。算法並非編程的前提條件,數學也不會阻礙一我的成爲優秀的程序員。至少在我看來,什麼算法基礎內功都是唬人的玩意,多編點能用的實用的程序纔是王道,固然若是你是一個pure theorist的話就當我什麼都沒說好了。
然而有意思的是,寫了這篇文章沒多久,鼓吹算法無用論的我本身作的幾個大大小小的項目所有用到了算法——我疑心是上天在有意抽個人臉。
我在微軟實習的第一個項目作的是代碼覆蓋率分析——計算T-SQL存儲過程的代碼覆蓋率。
簡單的看了下SQL Server相關的文檔,我很快發現SQL Reporting Service能夠記錄T-SQL的執行語句及行號,因而行覆蓋(line coverage)搞定,但老大說行覆蓋太naive,咱們須要更實際的塊覆蓋(block coverage)。
閱讀了塊覆蓋的定義後,我發現我須要對T-SQL進行語法分析,在沒有找到一個好用的T-SQL Parser的狀況下,只能本身動手搞一個:
比較奇詭的是,作這個項目時當時我恰好把ANTLR做者的Language Implementation Patterns看了一半,什麼LL(k)啊Packrat啊AST Walker的概念啊正熱乎着呢。
因而,本身本身就照着T-SQL的官方EBNF,三下五除二擼了一個T-SQL存儲過程的LL(k) Parser,把代碼轉換成AST,而後用一個External AST Walker生成代碼塊覆蓋的HTML報表,所有過程一週不到。
老大天然是很滿意——我疑心他的原計劃是花兩三個月來完成這個項目,由於這個項目以後的兩個月我都沒什麼活幹,每天悠哉遊哉。
拼音索引是我接的一個手機應用私活裏的小模塊,用戶期待在手機文本框能夠根據輸入給出智能提示:
好比說輸入中國:
一樣,輸入拼音也應給出提示:
中文匹配這個簡單,但拼音匹配就得花時間想一想了——懶得造輪子的我第一時間找到了微軟的拼音庫,但接下來我就發現微軟這個鳥庫在手機上跑不動,研究了下發現WP7對Dictionary的items數量有限制,貌似是7000仍是8000個item就會崩盤,而標準漢字則有兩萬多個,尼瑪。
痛罵MS坑爹+漢字坑爹之餘,仍是得本身擼一個庫出來:
用戶很happy——由於我捎帶把他沒想到的多音字都搞定了,並且流暢的一逼。
我也很happy,由於沒想到本身寫的庫竟然比MS的還要快幾十倍,同時小十幾倍。
從這個事情以後我變得特別理解那些造輪子的人——你要想一想,若是你須要一個飛機輪子但市場上只有自行車輪子並且老闆還催着你交工,你能怎麼搞。
前面提到在微軟實習時老大扔給我一個Windows Phone讓我研究下,我當時玩了玩就覺着不太對勁,找聯繫人太麻煩。
好比說找"張曉明",WP只支持定位到Z分類下——這意味着我須要在Z分類下的七十多個聯繫人(姓張的姓趙的姓鐘的等等)裏面線性尋找,每次我都須要滑動四五秒才能找到這個張姓少年。
這TMD也太傻逼了,本屌三年前的老破NOKIA都支持首字母定位,996->ZXM->張曉明,直接搞定,尼瑪一個新時代Windows Phone竟然會弱到這個程度。
搜了一下發現沒有好用的撥號程序,因而本屌就直接擼了一個支持首字母匹配的撥號程序出來扔到WP論壇裏。
結果立刻就有各類問題出現——最主要的反映是速度太慢,一些用戶甚至反饋按鍵有時要半秒纔有反應。本屌問了下他的通信錄大小:大概3000多人。
吐槽怎麼會有這麼奇葩的通信錄之餘,我意識到本身的字符串匹配算法存在嚴重的性能問題:讀取全部人的姓名計算出拼音,而後一個個的匹配——結果若是聯繫人數量太多的話,速度必然拙計。
因而我就開始苦思冥想有沒有一個可以同時搜索多個字符串的高端算法,以致於那兩天坐地鐵都在嘟囔怎麼才能把這個應用搞的快一些。
最終仍是在Algorithms on Strings, Trees and Sequences裏找到了答案——確實有可以同時搜索多個字符串的方法:Tries,並且這本書還用足足一章來說怎麼弄Multiple string comparison,看得我當時高潮迭起,直呼過癮。
具體細節很少說,總之換了算法以後,匹配速度快了大約九十多倍,並且代碼還短了幾十行。哪怕是有10000個聯繫人,也能在0.1秒內搞定,速度瓶頸就這樣愉快的被算法搞定。
以後又作了若干個項目,多多少少都用到了"自制"的算法或數據結構,最奇詭的一次是寫一個電子書閱讀器裏的分頁,我照着模擬退火(Simulated Annealing)的原理寫了一個快速分頁算法,事實上這個算法確實很快——但問題是我都不知道爲啥它會這麼快。
總之,算法是一種將有限計算資源發揮到極致的武器,當計算資源很富餘時算法確實沒大用,但一旦到了效率瓶頸算法絕壁是開山第一刀(由於算法不要錢嘛!要不還得換CPU買SSD升級RAM,肉疼啊!!)。一些人會認爲這種說法是有問題,由於編寫新算法的人力成本有時比增長硬件的成本還要高——但別忘了增長硬件提高效率也是創建在算法是Scalable的基礎上——說白了仍是得擼算法。
說到優化這裏順帶提一下Writing Efficient Programs——很難找到一本講代碼優化的書(我疑心是自從Knuth說了過早優化是萬惡之源以後沒人敢寫,萬惡之源嘛,寫它幹毛),注意這本書講的是代碼優化——在不改變架構、算法以及硬件的前提之下進行的優化。儘管書中的一些諸如變量複用或是循環展開的trick已通過時,但整體仍不失爲一本好書。
實習實習着就到了研二暑假,接下來就是求職季。
求職季時我有一種莫名的復仇感——尼瑪以前百度實習面試老子被大家黑的漫天飛翔,這回求職老子要把大家一個個黑回來,尼瑪。
如今回想當時的心理實屬傻逼+幼稚,但這種黑暗心理也起了必定的積極做用:我絲絕不敢有任何怠慢,以致於在5月份底我就開始準備求職筆試面試,比身邊的同窗早了兩個月不止。
我沒有像身邊的同窗那般刷題——而是繼續看書抄代碼學算法,由於我認爲那些可貴離譜的題面試官也不會問——事實上也是如此。
由於不少Coding Interview的論壇都提到這本紅皮書,我也跟風搞了一本。事實證實,僅僅是關於Backtrack Template那部分的描述就足以值回書價,更不用說它的Heuristics和課後題。
這兩本書就不用多介紹,編程珠璣和更多的編程珠璣,沒據說過這兩本書請自行面壁。前者偏算法理論,後者偏算法軼事,前者提高能力,後者增加談資,都值得一讀。
讀到編程珠璣裏面關於Binary Search的正確性證實時我大呼過癮,原來程序的正確性也是能夠推導的,而後我就在那一章的引用裏發現David Gries的The Science of Programming。看名字就以爲很厲害,直接搞了一本開擼。
不愧爲編程珠璣引用的書籍,擼完The Science of Programming以後,本屌得到了證實簡單代碼段的正確性這個技能——求職面試三板斧之二。
證實簡單代碼段的正確性是一個很神奇的技能——由於面試時大多數公司都會要求在紙上寫一段代碼,而後面試官檢查這段代碼,若是你可以本身證實本身寫的代碼是正確的,面試官還能挑剔什麼呢?
以後就是各類面試,詳情見以前的博客,總之就是項目經歷、紙上代碼加正確性證實這三板斧,摧枯拉朽。
求職畢業季以後就是各類Happy,Happy事後本屌發現即將面臨另外一個問題:算法能力不足。
由於聽說之後的同事大可能是ACM選手,而本屌歷來沒搞過算法競賽,並且知道的算法和數據結構都極爲基礎:像那些元胞自動機、斐波那契堆或是線段樹這些高端數據結構壓根只是能把它們的英文名稱拼寫出來,連用都沒用過,因此心理忐忑的一逼。
爲了避免至於到時入職被鄙視的太慘烈,加上本身一向的算法自卑症,本屌強制本身再次學習算法:
Algorithms是我重溫算法的第一本書,儘管它實際就是一本數據結構的入門書,但它確實適合當時已經快把算法忘光的本屌——不爲學習,只爲重溫。
這本書最大的亮點在於它把Visualization和Formatting作到了極致——也許它不是最好的數據結構入門書,但它絕壁是我讀過的排版最好的書,閱讀體驗爽的一逼;固然這本書的內容也不錯,尤爲是紅黑樹那一部分,我想不會有什麼書會比此書講的更明白。
Advanced Data Structures是MIT的高級數據結構教程,爲何會找到這個教程呢?由於Google Advanced Data Structures第一個出來的就是這貨。
這門課包含各類讓本屌世界觀崩壞的奇詭數據結構和算法,它們包括但不限於:
總之高潮迭起,分分高能,惟一的不足就是沒有把它們實現一圈。之後本屌必定找時間把它們一個個擼一遍。
從接觸算法到如今,大概七年:初學時推崇算法牛逼論,實習後鼓吹算法無用論,讀研後再被現實打回算法牛逼論。
怎麼這麼像辯證法裏的確定到否認再到否認之否認。
如今來看,至關數量的鼓吹算法牛逼論的人其實不懂算法的重要性——若是你連用算法解決實際問題的經歷都沒有,那你如何能夠證實算法頗有用?而絕大多數鼓吹算法無用論的人不過是低水平碼農的無病呻吟——他們從未碰到過須要用算法解決的難題,天然不知道算法有多重要。
Peter Norvig曾經寫過一篇很是精彩的SICP書評,我認爲這裏把SICP換成算法依然適用:
To use an analogy, if algorithms were about automobiles, it would be for the person who wants to know how cars work, how they are built, and how one might design fuel-efficient, safe, reliable vehicles for the 21st century. The people who hate algorithms are the ones who just want to know how to drive their car on the highway, just like everyone else.
MIT教授Erik Demaine則更爲直接:
If you want to become a good programmer, you can spend 10 years programming, or spend 2 years programming and learning algorithms.
總而言之,若是你想成爲一個碼農或是熟練工(Code Monkey),你大能夠不學算法,由於算法對你確實沒有用;但若是你想成爲一個優秀的開發者(Developer),紮實的算法必不可少,由於你會不斷的掉進一些只能藉助算法才能爬出去的坑裏。
以上。
By Lucida