数据结构与算法面试

前端工程师的 DSA 指南 - 需要了解的重要概念、要做的顶级练习题和其他技巧

作者
Ex-Meta Staff Engineer

算法编码问题正是您可以在 LeetCode 上找到的问题。 算法问题通常具有以下特征:

  • 它们并非特定于前端领域; 它们可以在大多数主流编程语言中解决。
  • 通常伴随着不切实际的场景。 您在现实世界的开发中不会遇到这样的问题。 谁曾经翻转过二叉树或计算过字符串中回文子串的数量?
  • 代码的效率(时间和空间复杂度)很重要,并且产生最有效的解决方案需要扎实的数据结构和算法知识。

尽管算法编码问题并非特定于前端,但要擅长这些问题所需的技能——强大的分析思维、有效的沟通、对常见数据结构和算法的扎实掌握、良好的代码卫生,仍然是优秀前端工程师应该具备的关键技能。 优秀的前端工程师也是优秀的软件工程师,而优秀的软件工程师应该掌握基本的 DSA。 因此,许多公司在面试过程中仍然会问算法编码问题也就不足为奇了。 熟悉数据结构和算法也有助于解决 JavaScript 编码问题和用户界面编码问题。

那里有大量涵盖算法编码面试的资源,由于它们并非特定于前端,因此我们不会在此页面上详细介绍。 如果您想了解有关算法编码面试的更多信息,我们建议您参考 Tech Interview Handbook 作为免费资源。

示例

  • 反转链表。
  • 确定字符串是否包含平衡括号。
  • 确定字符串中有多少个子字符串是回文。

如何准备

  1. 选择一种好的编程语言来使用。 如果您想节省准备时间,您可能应该坚持使用 JavaScript 来解决算法问题,尽管请注意,JavaScript 语言不包含某些常见的有用数据结构和算法,而 Python、Java 和 C++ 等其他语言则包含。 我们建议使用 Python 来解决算法面试问题
  2. 规划您的时间并按重要性顺序处理主题和问题
  3. 将学习和练习结合起来解决一个主题
  4. 伴随练习使用编码面试备忘单来内化必须做的事情和必须记住的事情

请参阅 Tech Interview Handbook 关于如何准备算法编码面试的分步指南

重要概念

尽管您仍然可能被问到任何算法问题,但公司倾向于对前端工程师候选人放轻松,并且可能不会问到涉及诸如动态规划或复杂图算法之类的难题。

由于 DOM 是一棵树,因此优先学习树和各种树遍历算法。

类别重要主题
数据结构数组、映射、堆栈、树、图、矩阵(2D 数组)、集合
算法二分查找、广度优先搜索、深度优先搜索、拓扑排序、递归

常见 JavaScript 操作

请注意常见的 JavaScript 操作及其时间复杂度。

Array

OperationTime complexity
Array.prototype.concat()O(m + n)
Array.prototype.every()O(n)
Array.prototype.fill()O(n)
Array.prototype.filter()O(n)
Array.prototype.find()O(n)
Array.prototype.pop()O(1)
Array.prototype.push()O(1)
Array.prototype.reduce()O(n)
Array.prototype.reverse()O(n)
Array.prototype.shift()O(n)
Array.prototype.slice()O(n)
Array.prototype.some()O(n)
Array.prototype.sort()O(nlgn)
Array.prototype.splice()O(n)
Array.prototype.unshift()O(m + n)

* n 是数组中的元素数量,m 是要添加的元素数量。

Map

OperationTime complexity
Map.prototype.clear()O(n)
Map.prototype.delete()O(1)
Map.prototype.entries()O(1) 因为它返回一个迭代器。获取所有条目将花费 O(n) 时间。
Map.prototype.forEach()O(n)
Map.prototype.get()O(1)
Map.prototype.has()O(1)
Map.prototype.keys()O(1) 因为它返回一个迭代器。获取所有键将花费 O(n) 时间。
Map.prototype.set()O(1)
Map.prototype.values()O(1) 因为它返回一个迭代器。获取所有值将花费 O(n) 时间。

* n 是 map 中的键的数量。

Set

OperationTime complexity
Set.prototype.add()O(1)
Set.prototype.clear()O(n)
Set.prototype.delete()O(1)
Set.prototype.entries()O(1) 因为它返回一个迭代器。获取所有条目将花费 O(n) 时间。
Set.prototype.forEach()O(n)
Set.prototype.has()O(1)
Set.prototype.keys()O(1) 因为它返回一个迭代器。获取所有键将花费 O(n) 时间。
Set.prototype.values()O(1) 因为它返回一个迭代器。获取所有值将花费 O(n) 时间。

* n 是集合中的元素数量。

算法编码面试标准

在算法编码面试中,面试官会根据以下技能评估候选人:

  • 解决问题:使用系统和逻辑的方法来理解和解决问题。将问题分解为更小的独立问题。评估不同的方法及其权衡。
  • 技术能力:将解决方案转化为可运行的代码,并展示对所用语言的深刻理解。
  • 沟通:提问以澄清细节,并清楚地解释自己的方法和考虑因素。
  • 验证:确定各种场景来测试代码,包括边缘情况。能够诊断和修复出现的任何问题。

有用的提示

  • 一厢情愿。JavaScript 的标准库没有一些有用的数据结构和算法,如队列、堆、二分查找,这可以在 JavaScript 编码面试中让你的生活更轻松。但是,你可以问面试官是否可以假装存在这样的数据结构/算法,并在你的解决方案中直接使用它,而无需实现它。
  • 纯函数。 旨在编写纯函数,这些函数具有可重用性和模块化的好处,也就是不依赖于函数之外的状态且不会引起副作用的函数。
  • 明智地选择数据结构。 注意你对数据结构的选择,并注意代码的时间复杂度。 熟悉基本的 JavaScript Array、Object、Set、Map 操作的时间/空间复杂度,以备你希望在你的解决方案中使用它们。 某些时间/空间复杂度在不同语言之间有所不同。 如果可以使用哈希映射在 O(n) 运行时内完成,请不要编写以 O(n2) 运行的代码。
  • 递归边缘情况
    • 如果你已经确定解决问题需要递归,请询问输入大小以及如何处理递归堆栈溢出的情况。 通常你不需要处理它,但提出这个问题是一个好信号。
    • 嵌套的深层数据结构可以对自身进行递归引用,这使得某些操作(如序列化)更加棘手。 询问面试官是否必须处理此类情况。 通常你不需要处理它,但提出这个问题是一个好信号。

练习题

Blind 75 是 Yangshun Tay 编写的著名且简洁的算法问题列表。你可以在 GreatFrontEnd 上的 Blind 75 问题列表 中练习。

GreatFrontEnd 还提供了一些 数据结构和算法的通用练习题,你可以在其中练习用 JavaScript 实现常见的数据结构(StackQueue)和算法(二分查找归并排序)等。