转自:http://zh.lucida.me/blog/on-learning-algorithms/html
严格来讲,本文题目应该是个人数据结构和算法学习之路,但这个写法实在太绕口——何况CS中的算法每每暗指数据结构和算法(例如算法导论指的其实是数据结构和算法导论),因此我认为本文题目是合理的。程序员
原文连接:http://zh.lucida.me/blog/on-learning-algorithms/面试
原文做者:Lucida算法
第一次接触数据结构是在大二下学期的数据结构课程。然而这门课程并无让我入门——当时本身正忙于倒卖各类MP3和耳机,对于这些课程根本就不屑一顾——反正最后考试划个重点也能过,因而这门整个计算机专业本科最重要的课程就被傻逼的我直接忽略过去了。编程
直到大三我才反应过来之后还要找工做——并且大二的折腾证实了我并无什么商业才能,之后仍是得靠码代码混饭吃,我当时惊恐的发现本身对编程序几乎一无所知,因而我给本身制订了一个相似于建国初期五年计划的读书成长计划,其中包括C语言基础、数据结构以及计算机网络等方面的书籍。网络
读书计划的第一步是选择书籍,我曾向当时我以为很牛的”学长”和”大神”请教应该读哪些算法书籍,”学长”们均推荐算法导论,还有几个”大神”推荐计算机程序设计艺术(如今我疑心他们是否翻过这些书),草草的翻了下这两本书发现实在看不懂,但幸运的是我在无心中发现了豆瓣这个神奇的网站,里面有不少质量不错的书评,因而我就把评价很高并且看上去不那么吓人的计算机书籍都买了下来——事实证实豆瓣要比这些”学长”或是”大神”靠谱的多得多。数据结构
数据结构与算法分析——C语言描述是我学习数据结构的第一本书:当时有不少地方看不懂,因而作记号反复看;代码看不明白,因而抄到本子上反复研读;一些算法想不通,就把它全部的中间状态全画出来而后反复推演。事实证实尽管这种学习方法看起来傻逼并且效率很低,但对于当时一样傻逼的我却效果不错——傻人用傻办法嘛,并且这本书的课后题大多都是经典的面试题目,以致于往后我看到编程之美的第一反应就是这货的题目不全是抄别人的么。架构
至今记得,这本书为了说明算法是多么重要,在开篇就拿最大子序列和做为例子,一路把复杂度从O(N3)杀到O(N2)再到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坑爹+汉字坑爹之余,仍是得本身撸一个库出来:
Int16
索引了汉字全部的拼音(貌似500多个)。Int64
创建汉字和拼音的关联——汉字有多音字,因此须要把多个拼音pack到一个Int64
里,这个简单,位操做就搞定。用户很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的高级数据结构教程,为何会找到这个教程呢?由于GoogleAdvanced 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