如何从容应对编程面试
“爱上这个过程,结果自然会来。”——埃里克·托马斯
“给我六个小时砍倒一棵树,我会用前四个小时磨斧头。”——亚伯拉罕·林肯
说实话,算法问题在求职过程中仍然占据着非常重要的地位。虽然现在越来越多的公司不再要求你经历繁琐的编程环节,但普通的开发者在求职过程中难免会遇到一些实际的算法挑战。尤其是如果你想在四大会计师事务所或成熟的初创公司工作的话。所以,我们还是会克服重重困难。
技术面试压力有多大,不用我说也知道。我相信大多数人都经历过那种沮丧的感受:面试失败后,我们一遍遍地想着所有可以扭转局面的方法。那种感觉真不好受。
这就是我写这篇文章的原因。对于那些最终参与算法挑战的人来说,你如何应对它至关重要。你是那种一头扎进去,然后一路摸索的人吗?还是你有一个流程,可以把问题分解成可处理的部分?虽然前一种方法可能对某些人有用,但我更倾向于后一种方法。
对我来说,掌握一套分解问题的步骤至关重要。虽然这并不能保证我能得到解决方案或工作机会,但它能让我更好地控制压力。将恐慌控制在可承受的范围内有助于我集中注意力。毕竟,技术面试应该展现你解决问题的能力,而不是你如何在不晕倒的情况下应对多个人默默地评判你。
在本文中,我想向大家展示我通过多次技术筛选和数十场模拟面试磨练出来的流程。它深受Launch School 的 PEDAC 系统的影响。我每次都用它,效果非常好。
“爱上这个过程,结果自然会来。”——埃里克·托马斯
展示我的流程的最佳方式就是实际操作。所以,让我们一起解决一个问题吧。为了尽可能真实,我会选择一个我从未解决过的问题。不过,你得相信我的话。
根据Leetcode的统计,字符串转整数算法是一道热门面试题。它的完成率也是所有中等难度题目中最低的。这应该是一个不错的挑战。
我选择这个问题也是因为它比较实用。这是一个在大多数编程语言中都已实现的实际算法。与许多其他面试题(比如Coin Change)不同,工程师们在实际生活中确实使用过这个算法。
话虽如此,让我们开始吧。您可以使用任何您喜欢的语言来继续学习。我将使用 JavaScript。您可以尝试我的方法,也可以使用您自己的方法。看看您是否能在我结束前解决问题。您最终可能会离创建我们自己的语言更近一步。
步骤 1:用你自己的话重新表述问题
对我来说,这是最重要的一步。这是我向面试官提问的机会,可以明确要求并解析所有关键信息。此外,将问题改写成我自己的语言,也让我有机会形成思维模型并消化问题。
对于这个问题,我想问的一个问题是,我是否可以使用类型转换。虽然描述中没有具体说明,但我只会使用 JavaScript 的原生类型转换来一次转换一个字符。这种限制在实际面试中我预料到会遇到。
阅读描述后,这些是我想出的关键细节。
// Given a string, return its appropriate number value.
// Ignore all white-space at the beginning of the string.
// Number may begin with a negative or positive.
// All characters that come after the number should be ignored.
// String is invalid if a character that is not a white-space or sign comes before the number.
// If string does not contain any integer values, it is invalid.
// The return value for any invalid string is 0.
// Resulting integer cannot be larger than (2^31) — 1 or smaller than -(2^31).
仅从这些需求来看,我已经开始构思如何创建这个算法了。它可能需要一些循环和相当多的条件逻辑。
有些人可能会在这一步之后开始写代码。对我来说,现在制定任何具体计划还为时过早——但我已经开始行动了。
步骤 2:输入和输出类型
很多人会觉得这步毫无意义,但我总是确保获取算法的输入和输出。要么作为代码注释,要么放在白板的角落里。
它有两个作用。首先,它明确了我的函数的参数以及签名的样子。Leetcode 已经帮我创建了函数签名,但在实际面试中不会出现这种情况。
其次,我会提醒自己要用到的类型。候选人因为忘记返回字符串而不是数组而导致所有测试用例失败的情况并不少见。我这么说或许是经验之谈,或许不是……
对于我们的问题,输入和输出在标题中定义得很好。
Input: string
Output: 32-bit signed integer
Signature: myAtoi(str)
步骤 3:示例和边缘案例
现在我对输入和输出有了信心,我想想出一些测试用例。这些示例需要涵盖我能想到的所有极端情况。我只能想象,有多少次候选人创建了一个可行的解决方案,却被面试官忽略了一个极端情况,导致他们的解决方案彻底失败。
你的面试官可能会提供一些,但我会提供更多——尤其是当它们不是详尽无遗的时候。例如,Leetcode 就给了我一些不错的测试用例。
In: “4193 with words”
Out: 4193
In: “words and 987”
Out: 0
In: “-91283472332”
Out: -2147483648
然而,这些例子忽略了一些可能性。如果数字以 + 开头会怎样?或者,如果数字前面有多个符号,比如 -+-50,会怎样?
让我们做出一些更好的。
Input: “+50.890”
Output: 50
Input: “ -+100”
Output: 0
Input: “ !another invalid -10”
Output: 0
步骤4:数据结构
大多数(即便不是全部)算法代码挑战都涉及使用结构体来跟踪数据。务必考虑使用哪种数据结构,因为它会影响您的实现。
从问题描述中我知道我将处理字符串和整数。但是我是否需要使用其他数据结构来帮助它们相互转换?
我已经预见到的一个问题是跟踪每个数字(十位、百位、千位等等)的位置。由于我事先不知道整数的长度,所以我将使用一个数组来跟踪整数字符。该数组将作为每个字符在转换为最终整数之前的临时占位符。
虽然很可能有更节省空间的解决方案,但我可以稍后再优化。现在,我只想选择对我来说最合理的方案。找到一个可行的简单方案总比一心想事成却一事无成要好。
步骤5:伪代码
我的倒数第二步是花些时间用伪代码阐述我的算法。面试官想了解你的思维方式和解决问题的方法。伪代码非常适合。
额外的好处是,面试官会提前知道如何帮助你。我曾经遇到过一些问题,面试官会提供一些巧妙的提示,让我继续下去。如果你没有计划就直接开始编程,最终可能会让自己和面试官都感到困惑。帮你们俩一个忙,制定一个行动计划。
这就是我想出来的。
// Start with index = 0
// While character at current index is white-space
// increment index
// Check if next character is invalid
// return 0
// Check if next character is positive or negative sign
// If negative sign, mark number as negative
// increment index
// Loop through characters starting at current index
// If current character is integer
// Unshift into front of array
// Increment index
// Else, break out of loop
// Loop through string integer array
// Cast string character into integer
// Multiply integer by (10^index) and add to return value
// If string contained negative sign, multiply result value by -1
// If result value is less than min, reassign to min
// If result value is greater than max, reassign to max
// return value
乍一看,我好像突然冒出这个想法,但背后却经历了大量的思考和反复试验。这是最耗时的一步,因为算法就是在这里创建的。
仔细阅读需求、输入/输出和边界情况。提出问题,澄清概念,并找出需要重点关注的不确定领域。找到你能想到的最简单的解决方案,并以此为基础开展工作。
你需要深度优先搜索吗?滑动窗口?分而治之?还是其他什么?
如果这是你最头疼的一步,别担心。多练习就会容易得多。你应该多练习。用伪代码进行彻底的算法设计,会让下一步变得快速而轻松。
第 6 步:代码!
“终于! ”你或许会想,“这花了好长时间啊! ”
确实,我花了很多时间进行规划。如果面试官给我45分钟完成面试,我会花15到30分钟进行思考和消化。
“给我六个小时砍倒一棵树,我会用前四个小时磨斧头。”——亚伯拉罕·林肯
事实上,编码对我来说是最重要的一步。所有繁重的工作都已经完成了。现在我只需要把我的思维模型解释成代码。
此外,我在面试中编写此解决方案的方式与我在现实生活中编写的方式不同。哎呀,真正的面试解决方案看起来可能与我为本文想出的答案不同。有几个因素会影响我在面试中的编码方式,例如面试官的时间和响应能力。
没有谷歌,也没有足够的时间重构,我只想写一些能用的东西。但我甚至无法保证我能实现这个目标。
但这不是这篇文章的重点。是的,我可能在面试中答不出来这个问题。但到目前为止,我已经把这个挑战分解成它的关键组成部分。我知道我能解决它,而且我已经把自己放在了最有利的位置。一个好的面试官会看到这一点。
在技术面试或现场面试中,重要的不是代码,而是你如何想出这个代码。
如果您有兴趣比较解决方案,以下是我提出的解决方案。
这个解法并不是最高效的。根据 Leetcode 统计,它只比另外 25% 的答案更有效率。
不过,它应该能通过大多数技术面试。面试官可能会要求我针对空间或时间进行优化,但如果时间允许,这些可以在后续的迭代中纳入。你不需要在第一次尝试时就想好。
使用流程的目的是为了系统地分解任何挑战。无论你是在日常工作中还是在技术面试中使用,它都有效。在面试中运用它,你可以通过专注于挑战而不是情绪来避免恐慌。
如果您还没有流程,那就开始制定一个吧。您可以使用PEDAC或自行开发。只要确保它能帮助您创建有信心的解决方案即可。
例如,你可能注意到我的解决方案中使用了常量、辅助函数和正则表达式。这些都是我总结出来的技巧,可以帮助我在面试中隔离复杂性。我的代码读起来越像英语,我写的时候就越不容易犯糊涂,工作速度也就越快。虽然可能有点冗长,但我喜欢。选择适合自己的就好。
如果您已经使用过某个流程,请练习并完善它。不要等到现场面试才开始微调。在模拟面试中尝试一下。Pramp和Interviewing.io就是完美的工具。
请记住,如果一切都失败了,请相信您的流程。
鏂囩珷鏉ユ簮锛�https://dev.to/sunnyb/how-to-approach-any-coding-interview-without-panicking-44fi