深浅色
刷题题单
暂停(10/08 - 10/29)。11/01 投递前只做文末的 30 题突击。 原因:时间优先给项目复盘和八股;笔试风险见
1.goal/周计划.md的风险提示。
面试统一用 JavaScript 写(前端 / 全栈岗默认语言,笔试平台也最顺手)。 这里只勾进度,具体错题写进
错题本.md。
30 题突击清单(10/30 - 11/01,只有 3 天)
只做这 30 道,别贪。目标不是刷完 Hot 100,是笔试别挂。 完美答案全部在
4.algorithms/答案/,每题含:题意 / 思路 / 复杂度 / 易错点 / 追问 / 可直接运行的 JS 代码(已跑过测试用例)。
| 模块 | 题数 | 答案文件 |
|---|---|---|
| 数组 / 双指针 | 5 | 01-数组与双指针.md |
| 滑动窗口 / 区间 | 5 | 02-滑动窗口与区间.md |
| 二叉树 | 6 | 03-二叉树.md |
| 链表 | 4 | 04-链表.md |
| 动态规划 | 5 | 05-动态规划.md |
| 手写题 | 5 | 06-手写题.md |
数组 / 双指针 / 滑动窗口(10) —— 答案 · 答案
- 两数之和
- 三数之和
- 移动零
- 盛最多水的容器
- 除自身以外数组的乘积
- 无重复字符的最长子串
- 找到字符串中所有字母异位词
- 最小覆盖子串
- 合并区间
- 轮转数组
二叉树(6) —— 答案
- 二叉树的中序遍历(迭代写法)
- 二叉树的层序遍历
- 二叉树的最大深度
- 翻转二叉树
- 二叉树的最近公共祖先
- 路径总和 III
链表(4) —— 答案
- 反转链表
- 合并两个有序链表
- 环形链表
- K 个一组翻转链表
动态规划(5) —— 答案
- 爬楼梯
- 打家劫舍
- 最长递增子序列
- 零钱兑换
- 编辑距离
手写题(5,不算刷题但必考) —— 答案
- LRU 缓存
- 限流器(令牌桶)
- Promise.all
- 防抖 / 节流
- 手写 Promise 的 then
三遍法(投递之后再启动)
- 第一遍 Hot 100:只求做出来,不限时
- 第二遍 Hot 100:限时 20 分钟,写不出就记错题
- 第三遍:只看题干说思路,卡壳的重新做
专题(投递之后再刷)
| 专题 | 代表题 | 状态 |
|---|---|---|
| 数组 / 双指针 | 三数之和、接雨水 | |
| 滑动窗口 | 无重复字符的最长子串、最小覆盖子串 | |
| 二分 | 搜索旋转排序数组、寻找两个正序数组的中位数 | |
| 链表 | K 个一组翻转链表、环形链表 II | |
| 二叉树 | 层序遍历、最近公共祖先、路径总和 III | |
| 回溯 | 全排列、组合总和、N 皇后 | |
| 动态规划 | 最长递增子序列、编辑距离、01 背包 | |
| 贪心 | 跳跃游戏、买卖股票的最佳时机 | |
| 图 | 岛屿数量、课程表(拓扑排序)、Dijkstra | |
| 堆 / 栈 | 前 K 个高频元素、柱状图中最大的矩形 | |
| 位运算 | 只出现一次的数字 | |
| 设计题 | LRU 缓存、LFU 缓存、限流器 |
JS 常用模板(背下来,现场不用想)
排序
js
arr.sort((a, b) => a - b); // 数字升序,不传比较函数会按字符串排
objs.sort((a, b) => a.age - b.age); // 按字段
arr.sort((a, b) => a[0] - b[0] || a[1] - b[1]); // 多关键字二分查找(找第一个 >= target 的位置,即 lower_bound)
js
let lo = 0;
let hi = arr.length; // 注意是 length,不是 length - 1
while (lo < hi) {
const mid = (lo + hi) >> 1;
if (arr[mid] < target) lo = mid + 1;
else hi = mid;
}
// lo 就是答案;lo === arr.length 表示没有 >= target 的元素BFS 层序遍历
js
const queue = [start];
const seen = new Set([start]);
let depth = 0;
while (queue.length) {
const size = queue.length; // 固定当前层
for (let i = 0; i < size; i++) {
const cur = queue.shift();
if (isTarget(cur)) return depth;
for (const next of neighbors(cur)) {
if (!seen.has(next)) {
seen.add(next);
queue.push(next);
}
}
}
depth++;
}DFS 回溯(子集 / 排列 / 组合的通用骨架)
js
const res = [];
const path = [];
const backtrack = (start) => {
res.push([...path]); // 收集结果要拷贝,否则会被后续修改
for (let i = start; i < nums.length; i++) {
if (i > start && nums[i] === nums[i - 1]) continue; // 去重
path.push(nums[i]);
backtrack(i + 1);
path.pop(); // 回溯
}
};
backtrack(0);手写小顶堆(JS 没有内置堆,面试常让你自己写)
js
class MinHeap {
constructor() {
this.a = [];
}
push(v) {
this.a.push(v);
let i = this.a.length - 1;
while (i > 0) {
const p = (i - 1) >> 1;
if (this.a[p] <= this.a[i]) break;
[this.a[p], this.a[i]] = [this.a[i], this.a[p]];
i = p;
}
}
pop() {
const top = this.a[0];
const last = this.a.pop();
if (this.a.length) {
this.a[0] = last;
let i = 0;
while (true) {
let m = i;
const l = i * 2 + 1;
const r = i * 2 + 2;
if (l < this.a.length && this.a[l] < this.a[m]) m = l;
if (r < this.a.length && this.a[r] < this.a[m]) m = r;
if (m === i) break;
[this.a[m], this.a[i]] = [this.a[i], this.a[m]];
i = m;
}
}
return top;
}
get size() {
return this.a.length;
}
}并查集
js
class DSU {
constructor(n) {
this.parent = Array.from({ length: n }, (_, i) => i);
this.rank = new Array(n).fill(0);
}
find(x) {
while (this.parent[x] !== x) {
this.parent[x] = this.parent[this.parent[x]]; // 路径压缩
x = this.parent[x];
}
return x;
}
union(a, b) {
const ra = this.find(a);
const rb = this.find(b);
if (ra === rb) return false;
if (this.rank[ra] < this.rank[rb]) this.parent[ra] = rb;
else if (this.rank[ra] > this.rank[rb]) this.parent[rb] = ra;
else {
this.parent[rb] = ra;
this.rank[ra]++;
}
return true;
}
}设计题(LRU / 限流器)在后端和前端面试里出现频率都很高,别只当算法题刷,要能讲清数据结构选型。