如何更好地应对编程面试
如何更好地应对编程面试
想提高面试技巧?关键在于方法——本指南将逐步讲解如何准确回答 Facebook、亚马逊、微软、Netflix 或谷歌等公司的编程面试问题。
本文将涵盖很多内容。它将引导你完成一个常见的技术面试问题(白板面试或非白板面试),你将接触到以下内容:
- 克服紧张情绪所需的心态
- 面试过程中的每一步
- 需要复习哪些图案
还有更多。让我们先解决面试中的心理问题,然后再采取切实可行的措施来解决这些问题。
克服紧张
软件工程和技术面试总是让人紧张。我们都知道这一点,但却很少问为什么。
为什么这类采访会让人产生如此恐惧的感觉?
确实,没有什么后果。
最糟糕的情况是,你无法正确表达他们想要的算法。或者你无法给出某个东西的正确定义。那么你很可能就得不到这份工作了。
这可不是什么好结果,但还有更糟糕的事情。你完全可以回家,补习一下错过的知识,然后尝试申请其他学校。
但知道这一点似乎没有帮助,原因如下。
我们的恐惧通常源于不确定性。也就是说,我们认为自己可能会遇到一个我们不知道如何解决的问题或挑战。
但事实并非如此!
首先,采用正确的方法(本文的其余部分将深入探讨),您可以解决任何问题。
其次——确实有可能你会被问到一些完全出乎意料的问题。但大多数情况下,面试官确实想了解你的想法。作为一个经历过多次面试的人,我知道我们希望你表现出色。
如果仍然感到紧张,还有其他解决方法。一些可能有帮助的方法包括每天冥想、吃健康的饮食来补充大脑能量,以及定期进行有氧运动。
如果你完全失去意识该怎么办
几乎任何技术挑战,只要方法得当,都能找到解决问题的方法。但如果你真的不知道从何入手,该怎么办呢?
因此,让我们快速解决一个问题——如果您真的不知道,请按照以下步骤操作。
假设你是一名前端工程师,拥有几年Javascript
单页应用开发经验。在一次面试中,你被问到以下技术问题。
何时会在两个后端系统之间应用异步通信?
你僵住了,胸口突然一阵紧缩。你从未接触过任何后端系统,也忘了这asynchronous
意味着什么。有几秒钟,你盯着面试官,思绪却飘忽不定。
以下是解决此问题的两种可能方法:
- 将其与你做过的事情联系起来
- 强调你对学习和从事这些事情的兴奋程度
第一个回应是有效的,因为它仍然允许您展示您的经验:
我没有直接开发过后端系统,不过你能再提醒我
asynchronous
一下“后端”是什么意思吗?嗯,一种允许工作独立于主应用线程进行的编程形式?我用 React 做过类似的事情——有时通过 REST 端点将数据插入数据库需要一些时间,但我们希望立即反馈给用户,告知数据正在被持久化。所以**我的猜测*是,当你想让一个进程在等待其他进程完成的同时执行某项操作时,它就变成了后端。*
在这种情况下,你所说的内容可能并非面试官 100% 想听到的,但你展现了一些技术敏锐度。你还可以加入一些关于过去经历的讨论。
另一方面,如果你根本找不到任何可以与这个问题联系起来的东西怎么办?不妨谈谈你有多兴奋,以及你将如何学习,这可能是一个不错的选择:
说实话,我不太确定,但我听说过这个术语,
asynchronous
而且很想了解更多关于后端系统的知识。这次采访结束后,我一定会去了解一下。您有什么推荐的书籍或文章可以作为入门吗?
白板算法面试好吗?
本课的剩余部分将讨论如何解决标准化数据结构和基于算法的问题。它仍然适用于一些小样本的挑战,例如“这里有一个基本设置,用一些脚手架实现一个 REST API ”。
是的,白板算法面试确实存在争议。然而,它仍然存在一些原因。首先,它向面试官发出了几个强烈的信号,例如:
- 候选人能否在他人面前清晰地思考(本课旨在解决的问题)
- 他们听起来像是已经为面试做好了准备吗(这是职业道德的信号)
- 他们是否具有合理的逻辑能力?
- 他们能区分好的解决方案和坏的解决方案吗?
- 他们对计算机科学基础知识的掌握如何?
其次,它们很容易大规模实施,如果你是一家每年需要招聘数千名员工的企业集团,这一点尤其重要。
是的,有家庭作业、专题采访,以及为期一周的“试用”期。我相信这些都是很棒的方法。
然而,“你能解决我面前的这个问题吗”data structure
和算法问题仍然是大多数软件公司的标准。
让我们想想如何解决这些问题。
优秀受访者的应对方法
在我们深入探讨之前,这里有一个重要的提示。为了使这一切顺利进行,你必须熟练掌握数据结构和算法。
你对这些话题练习得越多,就越容易将pattern
它们变成-词缀。面试的时候,你也能更容易地从记忆中回忆起它们。
当然, AlgoDaily 的高级课程和每日新闻简报问题是一个很好的起点。其他建议可以在我们关于如何准备技术面试的课程中找到。
综上所述,以下是我们推荐的解决白板问题的典型步骤。我们将花大量时间深入探讨每一个步骤。
- 运行几个(1-3)个示例输入来了解问题
- 通过询问人类如何做到这一点,快速解开暴力解决方案
- 将暴力破解方法与模式、数据结构或计算机科学技术联系起来
- 优化并再次运行步骤 1 中的相同测试用例
- 如果有时间,请指出边缘情况并改进问题
面试期间的沟通
面试很大一部分是对你的沟通能力的考验,这不是什么秘密。软件工程是一项团队运动。
孤独天才程序员的神话终究只是个神话。尤其对于那些需要数十万工程师参与的大型、复杂、影响深远的项目来说,更是如此。
您如何展现强大的沟通技巧?
你必须继续说下去——我再怎么强调也不为过。除非你需要完全安静的环境来思考——这没问题——否则你应该说出你的想法。
- 如果你遇到困难,请告知面试官
- 如果你不明白这个问题,可以提出更多澄清问题
- 如果你不知道发生了什么,就说你需要更多背景信息
- 如果您需要提示,请告诉他们!
如果你害羞,完全没问题。但说到面试,你要知道,你和这个人一起工作的可能是这个人,也可能是和你拥有类似资质和技术能力的人。无论好坏,面试官在面试中对你的看法,都决定了他们最终会如何看待你。尽量保持友好和坦诚的态度,哪怕只是为了争取这份工作所需的几个小时。
如何收集需求
让我们继续学习一些实用的技术面试技巧。我们可以看看“从零到末尾”的问题。题目如下:
编写一个方法,将数组中的所有零移动到其末尾。
你首先应该做的就是明确需求。除非你确切知道问题所在,否则你没必要去想解决方案。原因如下:
应聘者:太棒了,零在后面,非零在前面。是这样的吧?
看起来很简单。为什么不直接开始解决呢?因为面试官可能会说:
面试官:另外,请注意,你应该保持所有其他元素的顺序。哦,这需要在 O(n) 时间内完成。
如果你没有考虑到这一点,你可能会走上一条非常糟糕的道路。务必用自己的话重复问题,并进行深入的澄清。了解所有需求,并重复一遍,让他们知道你已经完全掌握了问题的全部内容。
候选人:所以我们想把所有零移到后面,同时把非零放在前面。我们还想保持非零元素原来的顺序,并且算法应该以线性时间运行。
采访者:完全正确。
从输入和输出开始
接下来要做的就是要么索要几个样本数组,要么自己设计一个。开始构建你的测试用例。这有助于你开始处理如何转换输入以获得输出。
尝试从非常小的输入开始,随着样本数量的增加逐渐增加其大小。你可以这样说:
候选人:这个问题很有意思。好吧,为了确保我理解了我们想要的转换和结果,我来举几个例子。如果我得到
[0, 1]
,我们想要[1, 0]
返回什么?面试官:是的,没错。(应聘者开始思考如何移动第一个例子中的那个零)
候选人:嗯,对于那个,我们只需要将
0
和 交换1
。现在,如果给出[1, 0, 2, 0, 4, 0]
,我希望[1, 2, 4, 0, 0, 0]
返回。采访者:正确。
候选人:然后
[0, 0, 4]
变成[4, 0, 0]
。嗯……
如何找到强力解决方案
现在您已经尝试了一些输入和输出,要问的主要问题是:
If a machine were not available, how would a human manually solve this?
记住,计算机只是工具而已。在它们出现之前,人类只能手动计算。所以,问问自己如何手动计算,是开始集思广益解决问题的好方法。当循环和条件语句不可用时,你可以用简单的英语表达你需要做什么。
候选人:嗯,是的,对于这些例子,结果总是返回一个非零数组,最后返回一个零数组。我思考了一个非常基本的实现,我一直在做的是:迭代,找到非零值,然后把它们放进另一个
temp
数组。然后,我一直用0
s 填充结果数组的剩余部分,直到得到原始长度。面试官:有意思。想把那个实现写出来吗?
使用伪代码来澄清你的想法
除非算法非常简单,否则您需要先编写伪代码。
对于暴力破解方案尤其如此。面试官可能第一遍只接受伪代码,然后可能会要求你用剩下的时间求解并编写一个优化方案。
此外,如果发现有害错误,用伪代码思考更容易修改。它最初的样子可能如下:
temp = []
zero_count = 0
iterate through array:
if nonzero, push to new temp
if zero, increment count
for zero_count times:
push to temp
return temp
如果面试官修改问题使其稍微复杂一些,这说明你的思路是对的。他们可能会添加约束(在常数时间内完成),或者显著增加输入量。根据我的经验,大多数面试官会计划做一道简单题和一道难题。
面试官:太好了,现在你可以在不实例化新数组的情况下做到这一点吗?
此时别慌,也别因为通过了第一部分而兴奋不已。现在该把我们的暴力破解方案与改进技术结合起来了。我们将介绍几种改进方法。
如何利用模式和抽象进行优化
完成大约 50-100 个模拟面试题后,你就会开始认识到可以利用的模式。这里有一个例子:If you want speed, you usually need more space/memory.
这与下一节关于如何使用数据结构的内容特别相关。
回顾目前为止解决方案中的每个步骤,思考是否有任何潜在的简化或分解方法。有什么方法可以降低其复杂性?
一个技巧是从更高的层次思考你正在做的事情。我的意思是,让你自己摆脱逻辑的束缚,回到输入到输出的过程。在上面的例子中,我们通过连接数组将零移动到末尾,但实际上我们需要做什么呢?这个过程可以理解为:
- 识别非零元素
- 将元素放在不同的索引处
- 找出
0
有多少个
拥有上述清晰步骤的好处在于,您现在可以探索完成每个步骤的其他方法。
- 例如,为了识别非零元素,您可以遍历数组并使用条件。
- 或者,您可以使用一种
filter
方法。 - 如果这没有帮助,您还可以
zeros
连续查找多个,然后splice
输出一个新的数组。
还要问自己一些问题:What am I trying to do in plain English?
取得进展的另一个非常简单的方法是尝试调整输入。
- 如果它是一个集合,排序或分组有帮助吗?
- 如果它是一棵树,我们可以将它转换成数组或链表吗?
如果调整输入没有产生任何影响,也许是时候进行更大的转变了。
引入数据结构或抽象数据类型
这时,学习数据结构(以及实现和使用它们的经验)就会非常有帮助。如果你能找出瓶颈所在,就可以开始尝试用数据结构来解决这个问题,看看是否能带来性能或空间上的提升。
回到我们之前做的“从零到末尾”的问题,我们的瓶颈很可能是步骤putting elements at different indexes
。在这种情况下,我们可能会意识到使用counter
变量是有益的。
注意,代码data structure
不需要太复杂。在我们的例子中,我们实际上只引入了一个int
变量——但有时这已经足够了。
那么应该计算什么呢counter
?好吧,一旦我们将数组拆分为非零值([1, 2, 3]
)和零值([0, 0, 0]
),我们实际上只关心非零值在哪里结束。
因为我们不需要担心数组中非零元素之后的内容,所以我们可以简单地保留一个单独的指针来跟踪最终数组的索引。它会告诉我们从哪里开始添加零。
然后我们可以编写以下伪代码来利用此策略:
insert_position = 0
for i in nums
if i is not 0
increase insert_position
keep it in insert_position
fill the rest in with 0
尽管有两个循环,时间复杂度仍简化为O(n)
。然而,由于我们使用相同的数组,空间复杂度是恒定的,所以我们得到了改进!
战术数据结构速查表
需要快速访问集合中的元素吗?数组已经在内存中保存了位置。
需要快速插入数据?将其添加到哈希表或链表。
需要在 O(1) 时间内求出最大值或最小值?那就用堆吧。
需要建模连接吗?那就去graph
那里吧。
数据结构掌握得越多越好。你可以查看课程表,了解绝对要求。
尝试一些更高级的结构也很有用(但并非必需),比如 AVL 树、字典树、优先级队列、后缀数组和布隆过滤器。虽然它们不太可能被用到,但在工业界却很有用,而且在面试中拿出来会给人留下深刻印象。
关于哈希表的特别说明:
好好了解一下这些家伙!它们可以用于数量惊人的解决方案。许多问题可以简化为在大型数据集合中搜索元素、查找重复项或存储/检索项目。哈希表/哈希映射在这些方面表现非常出色,所以请始终牢记它们。
如果其他方法data structure
没有帮助,那么也许是时候尝试一种老式(但可靠)的技术了。
介绍一种计算机科学算法技术
有一些技术是每个人都应该了解的。这些技术通常会在Intro to Algorithms
课程中讲解,以便对算法进行分类。
它们通常不仅对面试有用,而且对一般的软件工程工作也有益,所以要了解它们!
分而治之:尝试将问题分解成更容易思考或解决的子问题。这样可以……
递归——看看能否利用一个调用自身的函数。尤其要注意recursion
树形结构。
记忆化- 你在暴力破解中生成的部分结果是否可以用于更大或不同的输入?如果可以,可以利用某种缓存。你可以在内存中存储(或创建并存储在内存中)哪些数据来帮助算法?
贪婪graph
——思考每次迭代或每一步的最佳走法是什么。每一步是否有一个显而易见的最佳走法?这在像 Dijkstra 算法这样的遍历问题中经常出现。
如果以上方法都不起作用该怎么办
所以,上述任何模式、数据结构或技术都无法解决这个问题。该怎么办?
您有两个选择。
多问一些问题。
或者
我说,我卡住了。能给我点提示吗?
保持沟通!面试官通常很乐意给出提示——事实上,这是他们的工作。有些面试问题会很遗憾地包含一两个“关键直觉”,你必须先理解它们才能找到答案。
有了可行的解决方案之后
这里有一个关键点,需要花点时间才能搞定。在为优化方案编写了伪代码后,请逐步手动运行 1-3 个示例输入,确保伪代码能够正常工作。警告:你可能会感到紧张,并且有时会忘记进度,但这非常重要。
优秀的工程师会彻底测试他们的代码,并能够单步执行逻辑。在伪代码解决方案和在白板上写代码之间,正是展示这一点的好时机。
此时,您还可以消除错误的假设或做出重要的认识(“哦,等等,我们应该使用Map
而不是 JS 对象”)。
候选人:我们从 开始
[1, 0, 3]
,我认为这是一个很好的测试候选。好的,从 开始1
,我们看到它不是零,所以它可以保持不变。我们要转到下一个元素,所以让我们增加最终的数组索引计数器。现在我们有0
,我们跳过它。好的,3
不是零,我们的计数器在 ,1
所以我们把它放在 后面1
,这样就得到了[1, 3]
。太棒了!然后我们0
在末尾加上 a ,就得到了我们想要的结果。
面试官可能会说“太好了,我们开始写代码吧”,或者他们可能会想看看你对自己的解决方案有多自信。如果输入输出都通过了,你应该会感觉良好,可以继续前进了。
注意:如果您要在白板上进行面试,请购买磁性白板干板并在其上练习手写代码。
人们在白板上编码时没有考虑很多事情:主要是空间管理,还有如何使用较短的变量以及水平书写。
无论如何,您现在已经编写了以下代码:
function zerosToEnd(nums) {
let insertPos = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
nums[insertPos++] = nums[i];
}
}
for (let j = insertPos; j < nums.length; j++) {
nums[j] = 0;
}
return nums;
}
一旦您写出了解决方案代码,技术部分就应该完成了。
现在面试将转向面试官的问题。确保你准备好了问题,并尽量不要考虑你的表现。
到那时,无论发生什么,你都无法掌控。所以,要放眼未来,专注于面试官的当下。即使面试结果不如你所愿,保持镇定也会让面试官觉得你更专业。
希望本指南对您有所帮助。请记住,任何编程挑战都可以通过正确的方法和正确的心态来解决。祝您好运!
本课程最初发布于https://algodaily.com,我在那里维护技术面试课程并为雄心勃勃的开发人员撰写思考文章。
鏂囩珷鏉ユ簮锛�https://dev.to/jacobjzhang/how-to-get-better-at-approaching-coding-interviews-nnp