挣扎是真的
你是否一直在解决 LeetCode 问题,但感觉自己在面试中并没有真正进步?感觉您能够想出解决方案,但从未达到最佳解决方案?在您完成问题之前,您是否一直没有时间?您是否经常难以通过困难的问题?
你可能练习错了。
也许您一直在研究 Blind 75。或者,也许您只是在使用 Cracking the Coding Interview 中的示例问题。你可以从任何地方提出问题,我不在乎。如果你使用了错误的策略,无论你练习多少题,它都不会帮到你太多。
任何在健身房练过举重的人都知道良好的姿势和灵活性是必不可少的。这些元素对于充分利用您的锻炼是必不可少的。同样,在做 LeetCode 题时,您需要练习良好的问题解决技巧,以便最大限度地利用您花费的时间。
牢记真正的面试
你们中的许多人错误地练习了 LeetCode 问题,因为您可能不熟悉现实生活中的技术面试是如何进行的。让我们快速分解一下。
最重要的是,您需要了解技术面试是一种协作解决问题的练习。真正的面试不会是你自己解决 LeetCode 问题。你将要和一个真正的人交谈。说明这一点很重要,因为如果你自己练习,你可能没有考虑到你需要大声与他人交流并提出问题这一事实。
LeetCode 问题的最大弱点是没有人参与。该问题需要提供您预先解决问题所需的每一个细节。在真正的面试中,你的面试官可能会隐瞒一些信息,看看你如何处理歧义。换句话说,他们想知道你是否要问问题。
此外,如果你走错了方向,LeetCode 问题也无法给你提示。它无法就您的方法是否合理向您提供反馈。所有这些都很重要,因为你的面试官将评估你如何处理反馈。如果我,作为你的面试官,问你是否可以优化你的解决方案,我会期待比“我不知道”更好的答案。
哦,顺便说一句,您的面试官没有义务为您提供示例或测试用例。有些人这样做可能会有所帮助,这很好。但是如果你习惯于在你的 LeetCode 问题中提供所有这些,那么你将很难在真正的面试中拿出好的例子和测试用例。
更好的方法
建立必要的背景后,让我们谈谈我认为您应该如何正确研究示例问题。以下策略是我用来确保从每个面试问题中获得最大收益的策略。这些步骤将展示我如何应用我自己的问题解决框架(我称之为“六个步骤”)来练习面试问题。
1. 使用非IDE编码
在开始之前,请确保您已正确设置模拟面试。您可以选择使用白板、铅笔和纸,或者使用计算机和文本编辑器或轻型代码编辑器(如 Notepad++ 或 TextPad)来编写代码。尽量避免使用功能齐全的 IDE,因为它们通常包含您在真正的编码面试中不太可能获得的功能。
如果您与模拟面试官一起工作,请确保选择他们也可以看到和编辑的内容。
2. 决定是和模拟面试官一起练习(强烈推荐)还是自己练习
练习 LeetCode 问题的最佳方式是与另一个人一起评估你作为面试官。现在这会令人震惊,但他们不一定需要成为编码员!有一个软件工程师或开发人员同事当然是理想的,但重要的是找到一个你可以与之交谈并可以给你反馈的人。在为谷歌面试学习时,我依赖于我的妻子,她绝对不是技术人员,但对我的存在和举止提供了很好的反馈。
当然,有时候你需要一个人学习,这很好。重要的是你要对自己负责,不要给自己比模拟面试官在真实面试中付出的更多。
哦,无论你和谁一起工作,都要向他们表示感谢和赞赏。无论是金钱、咖啡、公开致谢还是礼品卡,您总是可以做些事情来补偿他们的时间。
3. 启动时钟
大多数采访的时间为 45-60 分钟。根据您对真实情况的期望,决定您将花多长时间进行模拟面试。记住,你想让事情尽可能真实。一旦你的时间到了,就停止工作。训练自己在时间限制内做好工作很重要。
这里需要注意的是,如果您还处于面试准备阶段的早期,您可能只想对自己进行几次基准测试,看看自己的表现如何。你仍然想给自己计时,但不要在特定时间停下来。继续前进,直到找到最佳解决方案或想不出其他任何事情。
4. 阐明问题
您的模拟面试官将首先大声朗读问题。在整个面试过程中,您在任何情况下都不要查看示例问题、示例或限制条件。完全依赖模拟面试官来获取解决问题所需的任何信息。这就是为什么在面试官讲话时做笔记是绝对必要的,这样您就不必反复询问他们已经透露的信息。
为确保您正确听到问题并掌握了所有细节,请用您自己的话将问题重复给面试官。如果您遗漏了任何细节或添加了一些不必要的内容,您的模拟面试官应该在继续之前通知您。
如果你自己练习,请将问题连同示例一起大声朗读一遍。完成后,将问题隐藏起来,然后用你自己的话重复这个问题,就好像你真的在和真人说话一样。做笔记,但避免写出完整的问题陈述。专注于帮助你记住你正在解决的问题的关键部分的简短陈述。
5. 提出问题并确认假设
这是任何面试的方便提示。始终以受访者的身份提问。即使你认为你对如何解决问题了如指掌,即使你以前解决过它——仍然要问问题。以下是您几乎可以针对任何技术问题提出的一些问题:
- 输入的数据类型和有效数据范围是什么?
- 输入是否适合内存?
- 我可以期望收到无效数据吗?
- 如何提供输入数据?
- 输入数据是否已经排序?
请记住,您应该避免提出问题陈述本身已经有答案的问题。然而,根据您对问题的理解,在面试官的听证会上重新陈述您的假设是很重要和值得鼓励的。例如,如果问题提到对整数进行排序,您可能会假设您将对 32 位有符号整数进行排序。清楚地说明该假设或询问情况是否如此。该问题实际上可能需要您对适合短整数的值进行排序,从而节省空间(或 64 位长整数,这显然需要更多空间)。
如果您独自练习或与非技术人员一起工作,则不会有人回答您的问题。
一旦你记录了所有的问题和假设,此时你可以查看 LeetCode 的限制并写下答案。
同样,尽量不要写太多。尽量使用最少的单词来捕捉要点,这样您就不必在脑海中保留细节。
6. 提出示例输入及其输出
此时,您应该开始研究示例。这一步有两点很重要。
- 第一,查看为您提供的任何示例,并对它们进行逆向工程,以弄清楚它们揭示了有关问题或其约束的哪些细节。
- 第二,添加几个你自己的例子来证明你对这些约束的理解或提出更多问题。
考虑示例的一种方法是将它们视为测试用例。如果您习惯性地难以想出好的测试用例,请阅读测试驱动开发 (TDD) 和单元测试作为面试准备的一部分。
给模拟面试官的注意事项。如果受访者提出了违反约束的示例,请立即告知他们。您有义务告知或提醒他们在这种情况下的限制,因为这些信息可能对他们解决问题至关重要。
同样,如果你是一个人学习,那么你不会有人可以就你的例子给你反馈。没关系。仍然提出示例并对您在此步骤中可能产生的任何未解决的问题做出最佳猜测假设。LeetCode 问题通常足够具体,这应该不难做到。
7. 集思广益解决方案并评估他们的 Big-O
您现在应该有足够的信息来开始提出问题的解决方案。这是橡胶与道路相遇的地方。你对算法和数据结构的所有研究应该为你生成两到三种解决问题的可行方法做好充分准备。如果你在这一步很挣扎,那就去看看书吧。你有更多的学习要做。
好的头脑风暴的第一步是很好地猜测 Big-O 中表达的最佳解决方案是什么样的。是否可能存在恒定时间和恒定空间的解决方案?为什么不?复杂度为 O(lg n) 的对数解如何?请记住,您总是在朝着最佳解决方案前进。
例如,如果我需要对一个数组进行排序,我很确定在最坏的情况下我不能比 O(n) 时间复杂度和 O(1) 空间复杂度更好。我声称线性时间复杂度是因为我可能必须至少查看输入的每个元素一次,我声称恒定空间是因为某些排序将元素移动到位,这样我就不需要使用单独的数据结构。
一旦您认为自己了解理想情况,良好头脑风暴的下一步就是提出解决方案并预测其时间和空间复杂性。请注意,您的第一个解决方案可能是一种不太理想的蛮力方法。它也可能是您可以快速想出的唯一解决方案。没关系!如果几分钟后你只能想到这些,那就继续吧。但是花点时间想想你能做些什么来接近理想的、最优的解决方案。
一些原则在这里可能会有所帮助:
- 您通常必须使用更多空间(内存)才能使某些东西运行得更快(反之亦然),因此如果您要优化速度,您可能需要使用数据结构。
- 如果您的任务是构建一个新的数据结构,您可能需要将两个相互补充的数据结构放在一起。这意味着在你的学习中,你应该努力了解每种数据结构的优缺点以及它们旨在解决什么问题。
同样,只记下您的方法以及预期的时间和空间复杂性。预先进行估算,即使您不确定!这可以帮助您在编写第一行代码之前浏览潜在的优化方法。您可能会发现,与您最初设计的解决方案相比,更优的解决方案即使不是更容易实施,也同样容易实施。
最后一件事。您将不得不选择一个解决方案以在下一步中开始实施。您可以从面试官那里获得关于他们希望您实施哪一个的意见。你也可以先实现最简单的版本,然后再编写一个最佳版本。希望您能找到一位出色的模拟面试官,他会在这里为您提供良好的指导。
8. 实施解决方案
现在是体验中最简单的部分。如果您觉得这不是最简单的,那么您需要花更多时间练习编写代码。我这么说是有实际原因的。您希望在此步骤中花费最少的时间,以便您可以将更多时间用于其他步骤。就像古老的谚语所说:“测量两次,切割一次。”
因为您处于人为约束的、时间限制的时间表中,所以您还应该避免使用伪代码编写。在现实生活中的面试中,伪代码通常不算数。这意味着您必须将所有伪代码转换为真实代码才能对其进行正确评估。我建议您不要伪编码,而是对您提出的算法做一些小笔记,而不是编写伪代码,因为那样可能会更快并提供相同的实用程序。
同样,我们谈论的是主要由大型科技公司使用的编码面试。如果您可以在带回家的作业上进行伪代码并在截止日期前完成,那就去做吧!但在大技术层面,我向你保证,编写代码将是工作中最简单的部分。了解您需要做的工作并审查您的设计可能是使您的工作具有挑战性的主要因素。
一些技巧:
- 在写代码之前先说你要做什么,然后再写代码。不要在没有先解释你在写什么的情况下编写代码。这样可以确保面试官知道你在想什么,如果你走错了路,面试官有机会打断你。
- 使用好的、清晰的变量名。更喜欢冗长(例如 indexCount 而不是 idxCnt)。在真正的面试中,您的面试官不会是唯一查看您的代码的人。可读性非常重要,这也是任何主要软件商店都进行代码审查的原因之一。
- 不要害怕为您的语言使用通用的 API、语法糖和通用的惯用风格。如果您有疑问,只需询问您的面试官您是否可以使用某些东西。更重要的是,了解幕后发生的事情,以便您了解任何性能损失。例如,根据您的语言,在 O(n lg n) 或 O(n2) 之间调用收集成本的 sort 方法。仅仅调用该方法不会神奇地将您的代码变成一个恒定时间的解决方案。
- 作为练习方案的一部分,凭记忆编写算法可以帮助您提高编程语言的能力,同时也可以加强您对算法本身的理解
请记住,我强烈建议不要使用 IDE 编写解决方案来练习问题。如果你决定使用 LeetCode 编辑器,请不要编译和运行测试。在大多数情况下,您实际上不会在真正的面试中得到它,即使您这样做了,您也会被诱惑将其用作拐杖,而不是正确地遍历代码并对其进行测试。时间到了后使用真正的编译器和单元测试!
如果您发现自己在实施过程中遇到困难,请使用示例并在纸上计算出从输入到输出的步骤。尝试在纸上想象你需要做什么,这样你就可以看到你的代码需要做什么,然后实现它。
9. 测试你的代码
键入最后一个分号或花括号后,您仍然没有完成。您可能至少有一个错误、拼写错误或缺少的变量需要修复。逐行浏览您的代码,并检查每一行是否按照您设计的方式执行。你应该有一个需要注意的几件事的心理清单:
- 未声明的变量
- 差一错误
- 反向条件(例如“<”而不是“>=”)
- 错误的变量名
- 空指针异常
10. 优化
如果您还没有找到最佳解决方案,请返回头脑风暴或实施您已经设计出的更佳解决方案。在计时器归零之前,您还有更多工作要做。继续前进!
结束你的模拟面试
无论您是与合作伙伴一起练习还是独自练习,请务必记录对您的表现的反馈。将您的所有学习记录在日记或笔记本上是组织技巧、资源和您自己的进步的一种非常有用的方式。我仍然保留着我的模拟面试日记,它反映了我为取得成功而付出的一些努力。
如果您与模拟面试官一起工作,您应该回答两个问题:
- 您对自己的表现有何看法,以及
- 在录用、未录用或犹豫不决之间,您的面试官如何评价您。
这两个问题的关键是弄清楚你的自我分配是否与面试官的一致。这将帮助您在准备时树立正确的期望,从而建立信心并了解自己是否正朝着正确的方向前进。
允许你的面试官空间在非技术性的事情上批评你。您是否在演讲中结结巴巴或使用了不合逻辑的话?你有没有发出任何应该避免的烦人的声音?你在长时间的采访中保持安静吗?这种反馈可能不会帮助你更好地编码,但它会帮助你更好地沟通,这是在技术面试中取得好成绩的一半挑战。
最后,既然你的面试已经结束,请随意将你的代码复制到 LeetCode 工具中,并确保它可以编译并通过测试。如果他们没有注意到您错过的任何边缘情况。我实际上建议复制到 IDE 中,以便您可以获得有关编译错误的更多详细信息或使用调试器逐步执行代码。记下失败或遗漏的边缘情况,以便您可以将它们添加到您解决的下一个编码问题的实施清单中。