[转]我是如何在 10 分钟内搞砸 IT 面试的
最近,我差点儿就拿下了 FAMGA(CSDN 编者注:国内有 BAT,国外有 FAMGA,即 Facebook、Apple、Microsoft、Google、Amazon。)的工作机会。通过了电话面试,代码笔试题也顺利通过了,但最终却在一对一或二对一面试中翻了船:
- 白板编程面试:算法;
- 技术面试:JavaScript,CS,React;
- 文化适应性:你们懂的;
- 第二次白板编程面试:回头再写一篇文章吧。
1. 问题出在哪里?
导致这次面试失败的原因可以归结为我犯下的几个错误。
我估计我通过了技术测试和文化适应性测试(假设如此吧),但我承认在白板编程测试阶段表现得很糟糕。
回想下那天的经历,我都不期待有什么好的结果。
真相是:我根本没有准备好做白板面试。
2. 白板面试
白板面试是一种混合的技术测试手段,能同时测试对知识的理解程度和社交能力。
来源:https://skillcrush.com/2016/03/29/rock-your-next-whiteboard-test/
这个应该是众所周知了吧?其实考察的不是编码能力,而是解决问题的能力,不会这个那个的其实也不要紧……
我是个前端开发,通常我不会写任何面试题中用到的算法,也不会在编码过程中把我的思路表达出来。绝大部分时间我都在写交互式 UI。
这种测试又能反映出候选人的多少情况?
有人甚至说:
在真实世界里,你几乎从来不会在编程过程中凭记忆写一段算法。绝大多数情况下总是会用已有的库,这个库经过完整的测试,并且已被许多开发者们验证是正确的。
唯一需要回忆算法的情况就是在大灾变之后……网上所有计算机的硬盘被毁,所有基础科学的学术论文和教科书也都烧成了灰……
我的意见和上面某个意见很相似,尽管我更倾向于相信这种技能(善于白板面试的技能)要求的是拥有一系列其他软技能和硬技能。
3. 客观原因
我先列个单子,尽管一些情况和你的情况相同,但我列出它们的主要原因是我得喘口气😄。
- 我不是用母语面试的。在别人的注视下非母语会变得更困难。
- 我是个自学的前端开发,非科班出身,没有学术准备。
- 我的职业生涯中经历过的面试并不多,有限的面试中白板面试的情况更少。
- 我不经常在公众场合讲话。很不幸,但至少目前来看没多少。
你可以说这些根本不是客观原因嘛。你说得对。
根据定义,客观原因是指我们无法控制的原因,但实际上我可以提高英语水平,学习更多的 CS,参加更多面试,更多地在公众场合讲话。
接下来,我将试着解答下本应该在那天完成的白板面试题,然后报告下进度。
4. 测试题
1 2 3 4 5 6 7 8 |
[<span class="">'Tokyo'</span>, <span class="">'London'</span>, <span class="">'Rome'</span>, <span class="">'Donlon'</span>, <span class="">'Kyoto'</span>, <span class="">'Paris'</span>] <span class="">// YOUR ALGORITHM</span> [ [ <span class="">'Tokyo'</span>, <span class="">'Kyoto'</span> ], [ <span class="">'London'</span>, <span class="">'Donlon'</span> ], [ <span class="">'Rome'</span> ], [ <span class="">'Paris'</span> ] ] |
题目就是这样。
一个城市的名称经过旋转后可能正好是另一个城市的名称。将所有匹配的城市名称放在同一组。
在工作中需要经常在数千行代码的迷宫中探索,但要想用这个简单得甚至搞笑的问题向那些人证明自己,有多困难?请系好安全带……
5. 假设这样进行面试
这部分是我的解题方法,包括代码和思路。谁知道还有没有别的方法!
我:非常感谢。我很高兴能为你解答这道题(微微鞠躬)
我:首先问个问题,字符是只能“旋转”还是可以任意随机组合?
拉里(为保护面试官的隐私,这里用了假名):只能旋转。第一个字符移动到最后一个。每次只能移动一个。
有许多评论给出了他们的解法。首先感谢大家,我会认真阅读的:)但找出完美答案并不是本文的重点。同时请注意我给面试官的问题。许多评论的解答忽略了这一点:字符只能一次旋转一个。单纯.split 再.sort 是不够的。如果这样做,那么 tokyo 和 yookt 这种组合就会被放在一组,但实际上是不对的。Tokyo 只能和以下几个匹配:okyoT,kyoTo,yoTok,oToky。看到了吧?5 个字符只有 5 中可能的组合。这是个提示。请继续看。
我:对!我想我首先要旋转每个城市的字符。Tokyo 会变成 okyoT,然后 kyoTo,嗯,赞!这样就有 kyoto 了。所以得写个函数来做旋转。
我:还需要循环输入的所有城市,并“旋转”字符,进行匹配,最后再进行分组。我发现输入和输出都是数组,两者都包含同样的值,唯一的区别就是两个数组的深度,一个是一维数组,另一个是数组的数组。
还是我:我来写一些伪代码来验证我的想法。稍后我还会做测试,因为我很优秀。😏
1 2 3 4 5 6 7 8 9 10 |
<span class="">function groupCitiesByRotatedNames(cities) </span>{ <span class="">// use a variable to contain the output.</span> <span class="">let</span> output = []; <span class="">// loop through each of the cities</span> <span class="">// rotate the name in any possible combination</span> <span class="">// check if the output contains a city that matches a combination</span> <span class="">// add that city to the array containing the match</span> <span class="">// otherwise add the city to the output as a new array</span> <span class="">return</span> output; } |
像明星一样自信的我:来伪测试以下我的伪代码(😛)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<span class="">const</span> input = [<span class="">"Tokyo"</span>, <span class="">"London"</span>, <span class="">"Rome"</span>, <span class="">"Donlon"</span>, <span class="">"Kyoto"</span>, <span class="">"Paris"</span>]; console.<span class="">log</span>(groupCitiesByRotatedNames(input)); <span class="">// That's how it would build up. The final output would be the last array:</span> <span class="">// [</span> <span class="">// ["Tokyo"]</span> <span class="">// ]</span> <span class="">// [</span> <span class="">// ['Tokyo'],</span> <span class="">// ['London']</span> <span class="">// ]</span> <span class="">// [</span> <span class="">// ['Tokyo'],</span> <span class="">// ['London'],</span> <span class="">// ['Rome']</span> <span class="">// ]</span> <span class="">// [</span> <span class="">// ['Tokyo'],</span> <span class="">// ['London', 'Donlon'],</span> <span class="">// ['Rome']</span> <span class="">// ]</span> <span class="">// [</span> <span class="">// ['Tokyo','Kyoto'],</span> <span class="">// ['London', 'Donlon'],</span> <span class="">// ['Rome']</span> <span class="">// ]</span> <span class="">// [</span> <span class="">// ['Tokyo','Kyoto'],</span> <span class="">// ['London', 'Donlon'],</span> <span class="">// ['Rome'],</span> <span class="">// ['Paris']</span> <span class="">// ]</span> |
现在开始实现算法。
首先是旋转城市名的字符的函数。建一个工具函数(Utility Function):
我:现在让代码更干净些(也更难懂些)。(迷之自信)
我:对了!我喜欢 reduce,要多用一些!(reduce 也更函数式一些,所以面试中要多使用😎)
reduce()函数将函数应用在累加器和数组的每个元素上(从左到右),将数组合并成单个值(MDN:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)。
最后的单个值就是输出。
总结一下这个算法的工作原理:
首先检查城市数组中的每个亨氏,然后旋转名字,再把每个组合与累加器的值进行匹配。
如果找到了匹配,则将城市名字添加到包含匹配值的数组中(使用 Array.splice),否则产生一个只包含当前城市的新数组。
当当当当!
放在一起!
Gist 在此:
https://gist.github.com/albinotonnina/e5eb9589f3a2322678b75461ac230181
这个解法并不是最好的,但确实是我那天想到的。(复杂度是二次多项式时间,你可能想要用线性的 O(n)时间的算法解决)。
6. 实际的白板面试
直接从解答开始。我说,好,我需要个 reduce 方法。便立即到白板上写代码,而没有先想想该怎么写。我确实有一点思路,基本上与上面说的相同,但我没有描述,没有向可怜的面试官解释这个算法。所以我在代码里迷失了方向,中途思路中断了好几次,还出现了语法错误,代码也是伪代码和真实代码的混合。这时已经没办法挽救了。这种面试行为会极大地损害面试的结果,特别是混乱发生在自己身上时。
7. 经验和教训
练习,练习,练习。
找一块白板,找个问题,对着空气讲出你的想法并写出来,然后反复练习。
练习白板面试,学习实际的面试过程。自己的面试过程,或者任何面试过程都可以。就像在吉他上学习唱歌,学习变魔术或危险的冰上动作一样。
你得准备一次演讲。白板面试就像演说一样。
白板面试就像一次充满变数的演讲。
可变的是面试题本身。
我以后要这样做:
- 首先花点时间分析需求(1 分钟)
- 如果有必要,确切地复述问题并获得答案(3 分钟)
- 暂停一下,确定解题方向(几分钟,不要怕片刻的沉默)
- 提出初始解决方案,并获得面试官的反馈(5 分钟)
- 暂停,选择解决方案(2 分钟)
- 写伪代码(5 分钟)
- 测试伪代码(5 分钟)
- 将伪代码变成真实代码(如果需要的话)
大约 30 分钟就能用愉快、有目标的方式解决面试题。
8. 结论
附录:
1. 这里有些关于本文的很有意思的讨论:https://dev.to/albinotonnina/how-to-lose-a-it-job-in-10-minutes-35pi
2. 资源
Evana Mp 写了篇很好的评论,她允许我把她总结的白板面试资源列表贴在这里。我觉得这些资源非常有用!
她说:我把我从许多网站上与此有关的问答中收集到的资源做了个列表,按顺序分别是书、网站和课程:
书籍:
- Cracking the coding interview
- Introduction to Algorithms
- Programming Interviews Exposed
- Clean code
- Algorithm Design Manual
- How to Think About Algorithms
- Programming Pearls
- Data Structures And Algorithms Made Easy In Java 2nd Edition 2nd Edition
网站:
- InterviewBit
- HackerRank
- Codewars
- Codefights
- AlgoExpert
- CarrerCup
- Interview Cake
- LeetCode
- Geeks for geeks
- Pramp
- HackerEarth
- HiredInTech
课程:
- Coderust 2.0
[source]我是如何在 10 分钟内搞砸 IT 面试的