面试经典算法题集锦——《剑指 offer》小结

从今年 3 月份开始准备找实习,到现在校招结束,申请的工作均为机器学习/数据挖掘算法相关职位,也拿到了几个 sp offer。经历这半年的洗礼,自己的综合能力和素质都得到了一个质的提升。

实话说对于未来去哪里,即将如何发展还没有清晰的规划。迷茫总是会有的,但这并不是停止脚步的理由。找工作是在漫长的职业生涯中时常出现的转折点,而学习和和积累是终生的任务。这里不打算对这一段时间的各项细节和收获展开太多讨论,后续将会专门进行整理。

本文主要是对《剑指 offer》这本面试经典进行一个小结,也是未来继续坚持刷题的一个开端。

在准备面试过程中手写 bug free 代码的环节是必不可少的。本人并非 ACM 出身,虽说在学校基础课程学得还不错,但对于刷题并无太多经验,一开始还是吃力的。慢慢的发现这一过程还是挺有意思的,虽然校招找工作已结束,但这刷算法题会成为我新的爱好。最近将《剑指 offer》这本经典的面试宝典梳理了一遍,并实现和整理了书中的所有题目,并在“牛客网”上提交测试以保证代码的正确性。

平时有时间也可以刷一刷 leetcodehihocoder,很多题目思考起来还是很有意思的。对于其他的课本,我觉得《算法导论》《编程珠玑》《编程之美》都是不错的经典教材,如果以后有时间也会一一对这几本书进行总结。

对于刷题,谈谈我的几点感受:

  1. 熟练一门编程语言很重要,这决定了你能不能将 idea 尽快实现并印证。在一开始可以先写一些 easy 的题,主要是将自己的编程语言练熟。本人主要使用 c++ 实现,其实算法涉及的结构不是很多,stl 基本就可以很好的 cover。
  2. 凡事开头难,坚持总是会有收获的。我建议刷题要有一个持续的过程,不要间断,最好一段时间集中攻克一件事情。比如 x 天完成一本书,或者一块内容,这样有助于集中精力突破。如果三天打鱼两天晒网,每次捡起来都很吃力,会形成恶性循环。题目刷多了以后会培养出感觉和思考问题的思路。
  3. 无论简单与否都要思考清楚再开始写代码。分析题目时可以将问题进行分解,还可以借助实例,做图抽象等方法帮助理解。
  4. 注意细节,写 bug free 的代码(应该成为一种追求)。实现代码过程中应该分析好输入输出,考虑好特殊情况和边界条件等。比如字符串转换成整数需要考虑:不合法输入,溢出等情况。使用指针时刻注意是否为空的判断等。
  5. 优美,鲁棒的高质量代码。不仅仅是追求完成功能,还应该对代码的布局,变量名见名见意,关键边界条件的检查等。代码是程序员交流的语言,因此应该写得优美
  6. 重复检查,人脑 debug。无论是否在手写代码的环境中,写完代码都应该养成重复 review 的习惯,自己跑几个测试用例,确定没有问题之后再把你代码交给面试官。
  7. 没有完美的代码,注意沟通完成满足需求的代码。每一个程序都可以看成是输入到输出的映射过程,而输入和输出总是有很多可能的,我们一个程序不可能 cover 掉大千世界,函数 function,也可以叫做“功能”,即完成一定任务的函数,这就需要我们通过沟通去了解用户的意图,将问题抽象,并基于某些假设去完成这样的 function。
  8. 解决问题的方法总是多样的。不管是做题还是平时练习,在完成任务的基础上应该还要发散性思考,一个题目的不同解法,相似题目的解法,相似场景的结局等。
  9. 一味的 practice,还要适时的总结和 review 才能及时的查漏补缺,让自己能更好的成长。

下面是课本中的一些总结,其实主要是经典语录。由于本人平时很喜欢记录一些大师/大神们的话,因此在总结中也是重点摘抄了很多大牛们的切身感悟和体验。有时候看看不仅是一种学习更是一种前人的激励。

第1章:面试的流程

面试官谈面试:

“对于初级程序员,我一般会偏向考察算法和数据结构,看应聘者的基本功;对于高级程序员,我会多关注专业技能和项目经验”——何幸杰(SAP,高级工程师)

“应聘者要事先做好准备,对公司近况、项目情况有所了解,对所应聘的工作真的很有热情。另外,应聘者还要准备好合适的问题问面试官。”——韩伟东(盛大,高级研究员)

“应聘者在面试过程首先要放松,不要过于紧张,这有助于后面解决问题时开拓思路。其次不要急于编写代码,应该先了解清楚所要解决的问题。这时候最好先和面试官多做沟通,然后开始做一些整体的设计和规划,这有助于编写高质量和高可读性的代码。写完代码后不要马上提交,最后自己review并借助一些测试用例来走几遍代码,找出可能出现的错误。”—— 尧敏(淘宝,资深经历)

“‘神马’,都是浮云,应聘技术岗位就是要踏实写程序。”——田超(微软,SDE II)

面试

面试的三种形式:

1.电话面试(听不清楚时要敢于说pardon)

2.共享桌面远程面试(最关心的应聘者的编程习惯以及调试能力,几种编程习惯:思考清楚再开始编码,良好的代码命名和缩进对齐习惯,能够单元测试。)

3.现场面试 (准备:规划好路线并估算出行时间,准备好得体的衣服,注意面试邀请函里面的面试流程,准备几个问题。面试:面试官通过应聘者的语言和行动,考察他的沟通能力、学习能力、编程能力等综合实力。)

面试的是三个环节:

1.行为面试(5~10min,过一遍简历内容)

1.1 自我介绍30s-1m

主要学习、工作经历以及意向,面试官若对某些细节感兴趣会提问

1.2 项目经历(注意“参与”vs “负责”):STAR模型描述

  • situation(简短的项目背景)
  • task(完成的任务)
  • action(为了完成任务做了哪些工作,怎么做的)
  • result(自己的贡献,用数字说话,多少功能?优化后提高百分比?修复了多少个bug?)

    例子:Winforms是微软.NET中一个成熟的UI平台(Situation)。本人的工作是在添加少量新功能之外主要负责维护已有的功能(Task)。新的功能主要是让Winforms的控件的风格和Vista, Windows 7 的风格保持一致。在维护方面,对于较难的问题我用WinDbg等工具进行调试( Action)。在过去两年中我总共修改了200个Bug(Result)。

    常见问题:(1)你在该项目中碰到的最大的问题是什么,你是怎么解决的?(2)从这个项目中你学到了什么?(3)什么时候会和其他团队成员(包括开发人员、测试人员、设计人员、项目经理等)有什么样的冲突,你们是怎么解决冲突的?

    要做好准备!!!!

    小提示:在介绍项目经验(包括简历和口述)时,应聘者不必详述项目的背景,而要突出介绍自己完成的工作以及取得的成绩。

1.3 应聘者掌握的技能(注意“了解” vs “熟悉” vs “精通”):

了解:对某一个技术知识上过课或看过书,但没有做过实际的项目。(通常不建议在简历中列出只是肤浅地了解一点的技能,除非这项技术应聘的职位的确需要。)

熟悉:(大多情况下使用)在实践项目中适用某一项技术已经有较长的时间,通过查阅相关的文档可以独立解决大部分问题,我们就熟悉它了。对于应届毕业生而言,毕业设计所用到的技能;对于已工作过的,在项目开发过程中所用到的技能。

精通:对一项技术得心应手,在项目开发过程中当同学或同事向我们请教这个领域的问题我们都有信心也有能力解决。(应聘者不要试图在简历中把自己修饰成“高人”而轻易适用“精通”,除非自己能够很轻松地回答这个领域里的绝大多数问题,否则就会适得其反。)

1.4 回答为什么跳槽:

了解应聘者的性格,可以大胆的回答自己真实的想法,但也不能想说什么就说什么,以免给面试官留下负面的印象。

避免一下几个原因:老板太苛刻,同事太难相处,加班太频繁,工资太低。

笔者在面试的时候,通常给出的答案是:现在的工作做了一段时间了,已经没有太多的激情了,因此希望寻找一份更有挑战的工作。然后具体论述为什么有些厌倦现在的职位,以及面试的职位我为什么会有兴趣。

第一次跳槽(从Autodesk -> 微软):我在Autodesk 开发的软件Civil 3D 是一款面向土木行业的设计软件。如果我想在现在的职位上得到提升,就必须加强土木行业的学习,可我对诸如计算土方量、道路设计等没有太多兴趣,因此出来寻找机会。

第二次跳槽(从微软->思科):我在微软的主要工作是开发和维护.NET的UI平台Windows。由于Windows已经非常成熟,不需要添加多少新功能,因此我的大部分工作都是维护和修改BUG。两年下来,调试的能力得到了很大的提高,但长期如此自己的软件开发和设计能力将不能得到提高,因此想出来寻找可以设计和开发系统的职位。同时,我在过去几年里的工作都是开发桌面软件,对网络了解甚少,因此希望下一个工作能与网络相关(思科是网络公司)。

2.技术面试 (40~50min)

5种素质:扎实的基础知识(编程语言、数据结构、算法等)、能写高质量的代码(正确的、完整的、鲁棒的)、分析问题时思路清晰(思路清晰分析和解决复杂问题)、能优化时间效率和空间效率(从时间和空间角度优化算法)、学习沟通等各方面的能力(沟通能力、学习能力和发散思维能力)。

3.应聘者提问(5~10min)

为每一轮面试准备2~3个问题。至少要问一两个问题。

不要问的问题:(1)不要问和自己的职位没有关系的问题,比如问“公司未来五年的发展战略是什么?”(2)不要问薪水,等过了技术面到HR面再说。(3)不要立即打听面试结果。

推荐问的问题:与招聘职位或者项目相关的问题,如果这类问题问得很到位,那么面试官就会觉得你对应聘的职位很感兴趣。从两个方面收集信息:(1)面试前要做足功课,到网上收集一些相关信息,做到对公司成立时间、主要业务、职位要求了然于胸;(2)面试过程中留心面试官说过的话。

比如:如何对新员工进行培训?需要掌握什么样的知识?

第2章:面试需要的基础知识

面试官谈基础知识

c++的基础知识,如面向对象的特性、构造函数、析构函数、动态绑定等,能够反映出应聘者是否善于把握问题本质,有没有耐心深入一个问题。另外还有常用的设计模式、UML图等,这些都能体现应聘者是否有软件工程方面的经验。—— 王海波(Autodesk,软件工程师)

对基础知识的考察我特别重视c++中对内存的适用管理。我觉得内存管理是c++程序员特别要注意的,因为内存的适用和管理会影响程序的效率和稳定性。—— 蓝诚 (Autodesk, 软件工程师)

基础知识反映了一个人的基本能力和基础素质、是以后工作中最核心的能力要求。我一般考察:(1)数据结构和算法;(2)编程能力;(3)部分数学知识,如概率;(4)问题的分析和推理能力。 —— 张晓禹(百度,技术经理)

我比较重视四块基础知识:(1)编程基本功(特别喜欢字符串处理这一类的问题);(2)并发控制;(3)算法、复杂度;(4)语言的基本概念。 —— 张珺(百度,高级软件工程师)

我会考察编程基础、计算机系统基础知识、算法以及设计能力。这些是一个软件工程师的最基本的东西,这些方面表现出色的人,我们一般认为是有发展潜力的。—— 韩伟东(盛大,高级研究员)

(1)对os的理解程度。这些知识对于工作中常遇到的内存管理、文件操作、程序性能、多线程、程序安全等有重要帮助。对于os理解比较深入的人对于偏底层的工作上手一般比快。(2)对于一门编程语言的掌握程度。一个热爱编程的人应该对某种语言有比较深入的了解。通常这样的人对于新的编程语言上手也比较快,而且理解比较深入。(3)常用的算法和数据结构。不了解这些的程序员基本只能写写“hello world”。 —— 陈黎明(微软, SDE II)

推荐的c++书籍:

《Effective C++》, 《C++ Primer》, 《Inside C++ Object Model》, 《The C++ Programming Language》

基础知识:

编程语言 + 数据结构 + 算法和数据操作

第3章:高质量的代码

面试官谈代码质量

一般会考察代码的容错处理能力,针对一些特别的输入会询问应聘人员是否考虑,如何处理。不能容忍代码只是针对一种遐想的‘正常值’进行处理,不考虑异常状况,也不考虑资源的回收等问题。 —— 殷焰(支付宝,高级安全测试工程师)

如果是因为粗心犯错,可以原谅,因为毕竟面试的时候会紧张;不能容忍的是,该掌握的知识点却没有掌握,而且提醒了还不知道。比如下面的:double d1, d2; .... if(d1 == d2) ... —— 马凌洲(Autodesk, Software Development Manager)

(解释:犹豫精度原因不能用等号判断两个小数是否相等)

最不能容忍功能错误,忽略边界情况。 —— 尹彦 (Intel, Software Engineer)

如果一个程序员连变量、函数命名都毫无章法,解决一个具体问题都找不到一个最合适的数据结果,这会让面试官印象大打折扣,因为这个只能说明他程序写得太少,不够熟悉。—— 吴斌 (NVidia, Graphics Architect)

我会从程序的正确性和鲁棒性两方面检查代码的质量。会关注对输入参数的检查、处理错误和异常的方式、命名方式等。对于没有工作经验的学生,程序正确性之外的错误基本都能容忍,但经过提示后希望能够很快解决。对于有工作经验的人,不能容忍考虑不周到、有明显的鲁棒性错误。 —— 田超 (微软,SDE II)

代码质量

代码的规范性: 清晰的书写、清晰的布局、合理的命名

代码的完整性: 3个方面保证代码完整性:功能测试、边界测试、负面测试 (溢出、递归正确退出、不合法的输入)。3种错误处理方法:返回值、全局变量、异常

. 优点 缺点
返回值 和系统api一致 不能方便地适用计算结果
全局变量 能够方便地适用计算结果 用户可能忘记检查全局变量
异常 可以为不同出错原因定义不同异常类型,逻辑清晰明了 有些语言不支持异常,抛出异常时对性能有负面影响。

代码的鲁棒性:采取防御式编程、处理无效的输入

第4章:解决面试题的思路

面试官谈面试思路

编码前讲自己的思路是一个考查指标。一个合格的应聘者应该在他做事之前明白自己要做的事情究竟是什么,以及该怎么做。一开始就编码的人员,除非后面表现非常优秀,否则很容易通不过。 —— 殷焰(支付宝,高级安全测试工程师)

让应聘者给我讲具体的问题分析过程,经常会要求他证明。 —— 张晓禹(百度,技术经理)

个人比较倾向于让应聘者在写代码之前解释他的思路。应聘者如果没有想清楚就动手本身就不是太好。应聘者可以采用举例子、画图等多种方式,解释清楚问题本身和问题解决方案是关键。 —— 何幸杰(SAP,高级工程师)

对于比较复杂的算法和设计,一般来讲最好是在开始写代码前讲清楚思路和设计。 —— 尧敏(淘宝,资深经理)

喜欢应聘者先讲清思路。如果觉察到方案的错误和漏洞,我会让他证明是否正确,主要是希望他能在分析的过程中发现这些错误和漏洞并加以改正。 —— 陈黎明(微软,SDE II)

喜欢应聘者在写代码之前先讲思路,举例子和画图都是很好的方法。 —— 田超(微软, SDE II)

复杂问题解决思路

画图 、举例子 、 分解

第5章:优化时间和空间效率

面试官谈效率

通常针对一些senior的candidates会问一些关于时间、空间效率的问题,这能够体现一个应聘者较好的编程素养和能力。 —— 刘景勇(Autodesk, 软件工程师)

面试时一般会直接要求空间和时间复杂度,这两者都很重要。 —— 张珺(百度,高级软件工程师)

我们有很多考查时间、空间效率方面的问题。通常两者都给应聘者限定,然后让他给出解决方案。 —— 张晓禹(百度,技术经理)

只要不是特别大的内存开销,时间复杂度比较重要。因为改进时间复杂度对算法的要求更高。 —— 吴斌(NVidia, Graphics Architect)

空间换时间还是时间换空间,这要看具体的题目了。对于普通的应用,一般是空间换时间,因为通常用户更关心速度,而且一般有足够的存储空间允许这么做。但对于现在的一般嵌入式设备,很多时候空间换时间就不现实了,因为存储空间太少了。 —— 陈黎明 (微软, SDE II)

一些小提示

c/c++程序员养成采用引用(或指针)传递复杂类型参数的习惯。如果采用值传递的方式,从形参到实参会产生一次复制操作。

循环和递归。循环一般效率较高,递归一般较为简洁。递归需要适用函数栈,严格来说分析的时候要考虑。

展现敏捷的思维能力和追求完美的激情。(先给出直观的办法,然后继续优化)

第6章:面试中的各项能力

面试官谈能力

应聘者能够礼貌平和、不卑不亢地和面试官交流,逻辑清晰、详略得当地介绍自己及项目经历,谈论题目时能够发现问题的细节并向面试官进行询问,这些都是比较好的沟通表现。对自己做的项目能够了解很深入、对面试题能够快速寻找解决方法是判断应聘者学习能力的一个方法。这两个能力都很重要,基本能够起到一票否决的作用。 —— 殷焰(支付宝,高级安全测试工程师)

有时候会问一些应聘者不是很熟悉的领域,看应聘者在遇到难题时的反映,在他们回答不出时会有人员提供解答,在解答过程中观察他们的沟通能力及求知欲。 —— 朱麟(交通银行,项目经理)

沟通能力其实整个过程都在考核,包括询问他过往的经历,也通常会涉及沟通能力。学习能力是在考查算法或者项目经验过程中,通过提问,尤其是一些他没有接触过的问题来考核的。沟通能力和学习能力很重要,在某种程度上这些都是潜力。如果应聘者沟通能力不行、难以合作,我们不会录取。 —— 何幸杰(SAP,高级工程师)

让其介绍过往项目其实就是在考查沟通和表达能力。学习能力通过问其看书和关注什么来考查。沟通能力、学习能力对最终面试结果会有一定的影响。对于资深的应聘者,影响要大些。—— 韩伟东(盛大,高级研究员)

应聘者会被问及一些需求不是很明确的问题,解决这些问题需要应聘者和面试官进行沟通,以及在讲解设计思路和代码的过程中也需要和面试官交流互动。沟通及学习能力是面试成绩中关键的考查点。 —— 尧敏(淘宝,资深经理)

沟通、学习能力就是看面试者能否清晰、有条理地表达自己,是否会在自己所得到的信息不够的情况下主动发问澄清,能否在得到一些暗示之后迅速做出反映纠正错误。 —— 陈黎明(微软,SDE II)

综合能力

编程能力、沟通能力、学习能力、知识迁移能力、抽象建模能力、发散思维能力....

第7章:题目一览表

面试思路:

链表:

递归和循环:

综合:

知识迁移能力:

树:

栈和队列:

查找和排序:

时间空间效率的平衡:

时间效率:

数组:

抽象建模能力:

字符串:

回溯法:

发散思维能力:

分解让复杂问题简单:

位运算:

代码的鲁棒性:

代码的完整性:

举例让抽象具体化:

上一篇:如何配置FTP服务器,方便操作服务器文件


下一篇:高通AR和友盟SDK的AndroidManifest.xml合并