Skip to content

本页是 JS/ 目录下所有 LeetCode 题解的完整检索与深度分析。该目录以 LeetCode 题目编号为文件命名核心,同时维护 JS 原始解法与 TS 类型化重写两套代码体系,部分题目还保留了 _1_2 后缀的迭代演进版本——从初始思路到优化方案的完整解题轨迹一览无余。无论你是想按编号快速定位某道题的解法,还是对比同一题目的多种思路差异,本页都能提供精确导航。

Sources: 1.js, 2.js, 3.js

目录结构与命名约定

JS/ 目录的文件命名遵循一套高度可预测的规则,掌握这套规则即可通过 LeetCode 题号直接定位到对应文件:

命名模式含义示例
{编号}.js题目的 JavaScript 初始解法1.js → 两数之和
{编号}.ts题目的 TypeScript 类型化版本19.ts → 删除链表倒数第N个节点
{编号}_1.js / {编号}_1.ts第一种替代/优化解法26_1.js → 删除有序数组中的重复项(双指针版)
{编号}_2.js第二种替代/优化解法2_2.js → 两数相加(链表逐位相加版)
q{编号}.js / q_{编号}.ts非标准编号题目q_1.ts → 板球计分逻辑
中文命名独立数据结构/算法实现堆.js归并排序.js排列组合模拟.js

Sources: 1.js, 2_1.js, 2_2.js, 26_1.js, 堆.js

txt/ 子目录存放的是早期通过 txt.bat 脚本批量从 .js 文件转换的文本备份,属于历史快照;Object/ 子目录包含 JavaScript 对象机制的实验代码;express/node-ts/ 是框架与 TS 运行环境的配置目录。

Sources: txt.bat, tsconfig.json, package.json

完整题目索引与分类

以下按算法思想对全部题目进行分类,标注各题的文件覆盖情况与核心解法策略:

哈希表与集合

题号题目JSTS核心策略
1两数之和1.jsMap 存补数 → O(n)
202快乐数202.js202.tsSet 检测循环
2540最小公共值2540.js2540.tsSet 交集
2657找到前缀子数组2657.js2657.tsSet 增量判重
347前K个高频元素347.jsMap 计频 + 排序
349两个数组的交集349.jsSet 去重交集

Sources: 1.js, 202.ts, 2540.ts, 2657.ts, 347.js

双指针与滑动窗口

题号题目JSTS核心策略
3无重复字符的最长子串3.js滑动窗口 + Map
15三数之和15.js排序 + 双指针
26删除有序数组中的重复项26.js / 26_1.js26.ts / 26_1.tsMap去重 / 双指针+splice
27移除元素27.js / 27_1.js27.ts / 27_1.tsSet+filter / splice循环
42接雨水42.js找最高点 + 左右双向扫描
220存在重复元素 III220.js220.ts窗口内遍历 + 剪枝

Sources: 3.js, 15.js, 26_1.js, 27_1.js, 42.js, 220.ts

链表操作

题号题目JSTS核心策略
2两数相加2.js / 2_1.js / 2_2.js数组模拟→链表模拟→逐位相加
19删除链表倒数第N个节点19.js19.ts两趟遍历求长度
21合并两个有序链表21.js递归合并
24两两交换链表节点24.ts递归交换
61旋转链表61.ts链尾链接到链头
82删除排序链表重复元素 II82.js / 82_1.js双指针跳过重复段
83删除排序链表重复元素83.js单指针去重
92反转链表 II92.js区间反转
133克隆图133.jsDFS + Map 深拷贝

Sources: 2_2.js, 19.ts, 24.ts, 82.js, 133.js

动态规划

题号题目JSTS核心策略
45跳跃游戏 II45.ts正向 DP:dp[i] = min(可达前驱的dp值) + 1
62不同路径62.ts网格 DP:dp[i][j] = dp[i-1][j] + dp[i][j-1]
64最小路径和64.js网格 DP:dp[i][j] = min(上,左) + grid[i][j]
70爬楼梯70.js / 70_1.js通项公式 / DP:dp[i] = dp[i-1] + dp[i-2]
134加油站134.js134.ts暴力枚举有效起点
198打家劫舍198.js198.tsDP:dp[i] = max(dp[0..i-2]) + nums[i]
740删除并获得点数740.js740.tsMap 聚合 + 打家劫舍模型
931下降路径最小和931.js矩阵 DP:三方向取最小
2770跳跃次数2770.ts正向 DP + Set 记录可达位置

Sources: 45.ts, 62.ts, 70_1.js, 198.ts, 740.ts, 931.js, 2770.ts

回溯法

题号题目JSTS核心策略
22括号生成22.js22.ts(空文件,预留)
46全排列46.ts经典回溯:used 数组标记 + path 记录
77组合77.ts回溯:start 参数控制起始位置

Sources: 46.ts, 77.ts

栈与表达式

题号题目JSTS核心策略
150逆波兰表达式求值150.js数组模拟栈 + splice 原地替换
224基本计算器224.js栈处理括号 + signal 符号变量
checkString括号匹配checkString.ts栈压入弹出验证

Sources: 150.js, 224.js, checkString.ts

堆与排序

题号题目JSTS核心策略
215数组中第K大元素215.js自实现最小堆 + 维护k容量
最小堆/最大堆堆.js完整堆实现(insert/up/down/swap/pop/peek)
归并排序归并排序.js分治递归 + merge合并有序数组
排列组合模拟排列组合模拟.js排列组合模拟.ts回溯法全排列

Sources: 215.js, 堆.js, 归并排序.js, 排列组合模拟.js

二叉树

题号题目JSTS核心策略
101对称二叉树101.js递归/迭代
110平衡二叉树110.js110.tsDFS 收集叶深 + 判断极差

Sources: 110.ts

数学与位运算

题号题目JSTS核心策略
4寻找两个正序数组的中位数4.js / 4_1.js4_1.tsconcat+sort / 二分分割线
7整数反转7.js逐位取模
9回文数9.js字符串反转比较
67二进制求和67.ts模拟加法 + 32位填充
788旋转数字788.js788.tsMap 分类 + 逐位校验

Sources: 4.js, 9.js, 67.ts, 788.ts

字符串与数组

题号题目JSTS核心策略
5最长回文子串5.js中心扩散法
14最长公共前缀14.js纵向扫描
28找出字符串中第一个匹配项28.tsindexOf(内置API)
48旋转图像48.tsMap 坐标映射
118杨辉三角118.js118.ts逐层构造
796旋转字符串796.ts拼接 + 截断
1861旋转盒子1861.js1861.ts旋转矩阵 + 重力掉落

Sources: 5.js, 28.ts, 48.ts, 118.ts, 1861.ts

TypeScript 专项练习(LeetCode TS 题库)

这些题目是 LeetCode TypeScript 专项题库的练习,侧重于类型系统与闭包设计而非传统算法:

题号题目文件核心技巧
2648斐波那契生成器2648.tsGenerator + 解构赋值 [a,b]=[b,a+b]
2665计数器 II2665.ts闭包 + type Counter 接口
2677数组分块2677.tsslice + 步长遍历
2704相等还是不相等2704.ts闭包 + throw Error 异常控制流
2706购买两块巧克力2706.ts两次 Math.min + splice
2618原型判断2618.tsinstanceof + Object() 装箱
Number_test整数除法截断Number_interger_test.tsMath.floor + 负数修正

Sources: 2648.ts, 2665.ts, 2704.ts, 2618.ts, Number_interger_test.ts

多解法对比:迭代演进模式

本仓库最独特的设计在于同一题目保留多个版本的解法文件,形成从"暴力直觉"到"最优策略"的完整解题演化链。以下通过三个典型案例深度剖析这一模式。

案例 1:两数之和(#1) — 哈希优化

1.js 中的注释完整记录了从暴力到优化的思维跃迁。最初的暴力思路是双重循环 O(n²),但文件实际已采用 Map 哈希表优化版本。核心逻辑是:遍历数组时,对每个元素 nums[i] 检查其是否已在 Map 中(即是否存在之前的元素需要当前值作为补数),若命中则直接返回,否则将 target - nums[i] 作为 key 存入 Map。这一策略将"查找配对"从 O(n) 降至 O(1),总复杂度从 O(n²) 降至 O(n),空间代价为 O(n)。

Sources: 1.js, txt/1.txt

案例 2:两数相加(#2) — 三版本演进链

这是本仓库中版本迭代最丰富的题目,完整展示了从"错误建模"到"正确实现"的三个阶段:

mermaid
graph LR
    A["2.js<br/>数组模拟法<br/>⭐ 2/5"] -->|审题修正| B["2_1.js<br/>ListNode 定义<br/>⭐ 1/5"]
    B -->|补充算法| C["2_2.js<br/>逐位相加+进位<br/>⭐⭐⭐⭐⭐"]
    style A fill:#ffcdd2
    style B fill:#fff9c4
    style C fill:#c8e6c9
  • 2.js:将链表错误地当数组处理,用 Math.pow 计算位权后转整数相加。致命缺陷是超出 Number.MAX_SAFE_INTEGER 时精度丢失,且完全违背了题目"链表操作"的本意。2.js
  • 2_1.js:正确定义了 ListNode 构造函数,但核心加法逻辑未实现,注释中给出了标准解法的伪代码。关键突破是引入了哨兵节点(dummy)概念。2_1.js
  • 2_2.js:完整实现了逐位相加 + 进位的标准解法。carry = Math.floor(v3 / 10) 取进位,v3 % 10 取当前位,while(p1 || p2) 处理链表不等长,最后的 if(carry) 处理末尾进位。2_2.js

Sources: 2.js, 2_1.js, 2_2.js

案例 3:删除有序数组中的重复项(#26) — 数据结构 vs 原地操作

两个版本的对比揭示了 LeetCode "原地修改"约束的本质区别:

维度26.js / 26.ts (Map版)26_1.js / 26_1.ts (双指针版)
数据结构new Map() 额外空间无额外数据结构
操作方式nums = [...map.keys()] 重赋值nums.splice(r, 1) 原地删除
是否原地修改❌ 创建新数组✅ 修改原数组
时间复杂度O(n)O(n²)(splice 每次移动后续元素)
LeetCode 评判⚠️ 可能不通过(重赋值断开引用)✅ 通过

关键认知:LeetCode 的"原地修改"要求调用者通过原引用看到修改结果,nums = [...] 赋值只是让局部变量指向新数组,调用者的原数组不受影响。而 splice 虽然效率不是最优,但确实是在原数组上操作。更优的原地方案应使用快慢双指针 + 覆盖而非 splice

Sources: 26.js, 26_1.js

案例 4:移除元素(#27) — 同题双策略

与 #26 类似的模式:27.js 使用 Set + filter 组合(非原地),27_1.js 使用 splice 循环删除(原地)。TS 版本完整复刻了两种策略。

Sources: 27.js, 27_1.ts

JS 与 TS 代码对照:类型系统的影响

同一题目的 JS 和 TS 版本并非简单的"加类型注解",而是体现了类型约束对代码结构的深层影响。以下从三个维度分析:

维度 1:链表节点定义的差异

JS 版本使用函数构造器,TS 版本使用 class + 属性声明:

javascript
// JS (19.js)
function ListNode(val, next) {
    this.val = (val === undefined ? 0 : val)
    this.next = (next === undefined ? null : next)
}
typescript
// TS (19.ts)
class ListNode {
    val: number
    next: ListNode | null
    constructor(val?: number, next?: ListNode | null) {
        this.val = (val === undefined ? 0 : val)
        this.next = (next === undefined ? null : next)
    }
}

TS 版本必须声明 val: numbernext: ListNode | null,这迫使开发者在编写算法逻辑时显式处理 null 的情况——例如 p1.next ? p1.next : null 而非直接 p1.next,从编译期就消除了潜在的空指针风险。

Sources: 19.js, 19.ts

维度 2:类型注解驱动的防御性编程

27_1.tsfunction removeElement(nums: number[], val: number): number 的签名明确约束了入参和返回值类型,对比 JS 版本的隐式 any,TS 编译器在调用端就能发现类型不匹配。在 740.ts 中,map.get(i) 返回 number | undefined,这提醒开发者需要用 map.get(i) ? map.get(i) : 0 做空值防护。

维度 3:编译产物与源码的关系

值得注意的是,19.js 实际上是 19.ts 的 TypeScript 编译产物(文件头包含 "use strict"Object.defineProperty(exports, "__esModule", { value: true })),而 1.js 则是手写的 JS 原始解法。这意味着部分 .js 文件和 .ts 文件并非"同一思路的两种写法",而是源码与编译产物的关系。区分方法是观察文件头是否包含编译器注入的样板代码。

Sources: 19.js, 27_1.ts, 740.ts

核心数据结构实现:最小堆与最大堆

堆.js215.js 中包含了一套完整的堆实现,是本仓库中少有的"数据结构先行、算法应用"的案例。这套实现覆盖了堆的六个核心操作:

操作最小堆实现复杂度
insert(val)push 到末尾 + up() 上浮O(log n)
up(index)与父节点 (index-1)>>1 比较交换O(log n)
down(index)与较小子节点比较交换O(log n)
pop()交换堆顶与末尾 + 删除末尾 + down(0)O(log n)
peek()返回 heap[0]O(1)
size()返回 heap.lengthO(1)

这套堆结构在 215.js 中被用于求解"数组中第K大元素":构建最小堆后不断 pop() 直到堆容量为 k,此时堆顶即为第 K 大元素。最小堆与最大堆的差异仅在于 up()down() 中的比较方向(< vs >)。

Sources: 堆.js, 215.js

自评注释体系:从错误中学习

本仓库大量题解文件头部的注释块构成了一套自评体系,包含评分、优缺点分析、优化思路和复杂度对比。这套体系的核心价值在于:它不只是记录"正确答案",而是诚实地标记了思维过程中的盲点和误区

4.js 为例,注释明确指出当前解法 concat + sort 虽然逻辑正确,但浪费了"已排序"这一关键条件,时间复杂度 O((m+n)log(m+n)) 远高于最优的 O(log(min(m,n))) 二分分割线法。再如 2.js 中标注了"审题错误"——把链表当数组用,以及 JS 数值精度问题。

这种"保留错误、标注改进"的文档风格,使得每份代码都成为一个可追溯的学习节点,而非仅仅是一份正确答案的快照。

Sources: 4.js, 2.js, 5.js

非标准题目与辅助文件

除了按 LeetCode 编号组织的题目外,JS/ 目录还包含一些非标准编号的练习文件:

文件内容特点
q1.js数字位校验函数存在循环条件 bug (i<n_str 应为 i<n_str.length)
q2.js双调数组求和比较数组切割 + reduce 求和
q_1.ts板球计分逻辑条件分支 + 类型判断
q_2.ts交替二进制串模式串生成 + 差异计数
1665.ts完成任务最小初始能量排序 + 贪心模拟
2615.js数组等距元素和O(n²) 暴力遍历
3761.js镜像对最小距离反转数字 + 双重循环
归并排序.js归并排序实现分治递归 + 合并
排列组合模拟.js全排列回溯经典回溯模板

Sources: q1.js, q2.js, q_1.ts, q_2.ts, 1665.ts, 归并排序.js

TypeScript 编译环境

TS 文件的编译环境由 tsconfig.json 配置,核心参数:target: ES2015(编译到 ES6)、module: commonjs(Node.js 模块系统)、strict: true(全量严格模式)。依赖仅 typescript: ^6.0.3,通过 npx tsc 即可编译 .ts 文件到对应的 .js 产物。

Sources: tsconfig.json, package.json

延伸阅读