数据结构层面,大家需要掌握以下几种:
- 数组
- 栈
- 队列
- 链表
- 树(这里我们着重讲二叉树)
对于这些数据结构,各位如果没有大量的可支配时间可以投入,那么其实不建议找厚厚的大学教材来刷。此时此刻,时间为王,我们追求的是效率的最大化。
不同的数据结构教材,对数据结构有着不同的划分、不同的解读、不同的编码实现。在这里,我们面向 JavaScript,面向前端面试,只针对大家后续做题、答题时会用到的最贴合实战的数据结构特性&编码技能作讲解。
- 这两节我们所提及的基础知识细节,很可能会成为你后面写代码的关键线索。
- 不要因为乍一看觉得简单,就急着跳读急着做题。
- 不然你很可能做题做到一半,会不知道自己到底为什么就卡了壳。
- 到时候万一又因为懒得回头看,而原地卡死,那就更做不下去了orz。
注:由于 JavaScript 中字符串和数组关联紧密,关键知识点重复度较高,故我们在数据结构部分,不再单独为字符串保留篇幅。字符串相关的知识点,我们直接带到后续的解题技巧归纳专题里去看。
# 数组
⚡ 30 秒速记
- JavaScript 数组是按整数索引组织的动态容器,不保证像 C 语言数组那样把所有元素连续存成同一种值
- 算法题里最重要的不是 API 数量,而是索引访问期望
O(1)、尾部增删通常O(1),头部插删需要搬移元素所以是O(n) new Array(n)创建的是稀疏数组,空槽与值为undefined不完全等价;要初始化数值状态可用Array(n).fill(0)fill([])会把同一个数组引用填进所有位置;二维数组应使用Array.from({ length: rows }, () => Array(cols).fill(0))- 遍历矩阵前先确认是不是规则矩阵;不规则数组每行长度不同,内层边界必须取
matrix[row].length
JavaScript 的 Array 是按整数索引访问的动态容器,不能简单等同于 C 语言中元素同类型且内存连续的数组。 算法题通常把索引读写按期望 O(1) 分析,尾部增删通常也是 O(1),而头部插删会影响后续索引,一般是 O(n)。new Array(n) 产生空槽;初始化二维数组时要为每一行单独创建数组,避免 fill([]) 共享引用。遇到不规则矩阵,内层边界还要使用当前行的 length。
先补一个算法面试里常被忽略的前提:JavaScript 的 Array 是语言抽象,V8 会根据元素类型和稠密程度选择不同内部表示,所以不能把“底层永远是一段连续内存”当成通用答案。但在解题模型中,按索引读取仍按期望 O(1) 分析;shift() / unshift() 会影响后续索引,通常按 O(n) 处理。
下面这段代码可以直接验证“空槽”和显式 undefined 的差异,以及二维数组正确初始化方式:
const sparse = new Array(3)
const explicit = [undefined, undefined, undefined]
console.log(0 in sparse) // false:0 号位置是空槽
console.log(0 in explicit) // true:位置存在,只是值为 undefined
console.log(sparse.map(() => 1)) // [ <3 empty items> ],map 会跳过空槽
const rows = 2
const cols = 3
const matrix = Array.from({ length: rows }, () => Array(cols).fill(0))
matrix[0][0] = 7
console.log(matrix) // [[7, 0, 0], [0, 0, 0]]