项目算法索引
这里按问题类型整理项目中的算法实现。每行的 高亮文字 是先记住的规则;源码路径可直接在编辑器中打开。首页的 逐步实验 适合先观察冒泡、选择、插入排序和 0/1 背包,再从这里继续看其他实现。
排序
| 算法 | 关键知识点 | 时间与空间 | 源码 |
|---|---|---|---|
| 冒泡排序 | 相邻交换,每轮确定一个有序后缀;相等时不交换,稳定。 | O(n²),额外 O(1) | src/baseSort/bubbleSort.ts、src/js/sort.js |
| 优化冒泡 | 每轮重新记录是否交换;整轮无交换即可结束,最好 O(n)。 | 最坏 O(n²),额外 O(1) | src/js/sort.js 的 bubbleSort2 |
| 选择排序 | 扫描未排序区间的最小值,每轮最多交换一次;比较仍为 n(n−1)/2 次,通常不稳定。 | O(n²),额外 O(1) | src/baseSort/selectionSort.ts、src/js/sort.js |
| 插入排序 | 暂存当前值、右移更大的值;有序前缀逐轮扩大,相等时不移动,稳定。 | 最好 O(n),最坏 O(n²),额外 O(1) | src/baseSort/insertionSort.ts、src/js/sort.js |
| 快速排序 | 按基准值分区,递归处理左右两边;这里选首元素,已排序输入会退化。 | 平均 O(n log n),最坏 O(n²) | src/baseSort/quickSort.ts、src/js/sort.js |
| 堆排序 | 最大堆根节点是当前最大值,与末尾交换后缩小堆。 | O(n log n),递归栈 O(log n) | src/js/sort.js 的 heapSort |
| 归并排序 | 先分后合;合并相等值时先取左边,保持稳定。 | O(n log n),额外 O(n) | src/js/sort.js 的 mergeSort |
| 希尔排序 | 逐步缩小间隔,最后用 gap=1 完成插入排序;不稳定。 | 此间隔序列最坏 O(n²),额外 O(1) | src/js/sort.js 的 shellSort |
排序数组允许重复值和负数。src/baseSort 可直接用于手写练习,首页的逐步演示独立运行,练习未完成也不影响展示;src/js/sort.js 保留多种方法,便于比较原位排序与返回新数组的做法。
动态规划与数值
| 算法 | 关键知识点 | 边界与代价 | 源码 |
|---|---|---|---|
| 0/1 背包(二维) | 选或不选第 i 件,只从上一行转移。 | 不要求装满;O(nC) 时间和空间 | src/js/dp2.js 的 knapsack |
| 0/1 背包(一维) | 容量倒序,确保 dp[c−w] 尚未使用当前物品。 | 同一件只能选一次;O(nC) 时间、O(C) 空间 | src/js/dp2.js 的 knapsack2 |
| 最长严格递增子序列 | dp[i] 必须以第 i 项结尾;答案是所有状态的最大值。 | 相等不算递增;空数组返回 0;O(n²) | src/js/dp2.js 的 getLongSeries |
| 爬楼梯 | 最后一步来自前一级或前两级,两种走法相加。 | 0 级视为一种空走法;O(n) 时间、O(1) 空间 | src/js/dp1.js 的 climbStairs |
| 最少硬币数 | 不可达状态为 Infinity;每种硬币可重复使用。 | 无解返回 −1;O(金额 × 面额数) | src/js/dp1.js 的 getCoinCount、coinChange |
| 硬币组合数 | 先硬币、后金额正序,避免把不同顺序重复计数。 | 金额 0 有一种空组合;O(金额 × 面额数) | src/js/dp1.js 的 getCoinList |
| 斐波那契 | 只依赖前两项;朴素递归重复计算,记忆化和迭代可降到 O(n)。 | f(0)=0;朴素递归限 0~40,其余实现精确计算 0~78 | src/js/fab.js、src/ts/fab.ts |
| 每堆取 1 或 2 枚 | 各堆独立,答案是 Σ ceil(每堆数量 / 2)。 | 空列表为 0;O(堆数) | src/js/xCoins.js |
| 大数相加 | 从低位逐位相加并传递进位,不把整个输入转成 Number。 | 只接受非空十进制数字串;O(较长输入位数) | src/ts/bigNumberPure.ts、src/ts/bigNumber.ts |
背包中的 dp[c] 表示“容量不超过 c 的最大价值”。正序更新会读到本轮刚写过的状态,等价于允许重复选当前物品;首页实验可切换方向直接对照。
数据、搜索与结构
| 算法 | 关键知识点 | 边界或限制 | 源码 |
|---|---|---|---|
| 路径建树 | 先建所有前缀节点,再按 parentId 连接;重复路径只建一次。 | 叶子节点 children 为 null | src/js/arrayToTree.js |
| 关键词邻行搜索 | 按原文行号选前、当前、后一行,再按行内容去重。 | 忽略空行;保持原文顺序 | src/js/findWord.js 的 findWord |
| 流式关键词搜索 | 记住上一行并等待下一行;末尾无换行时要冲洗缓冲区。 | getResult() 冲洗末尾片段 | src/js/findWord.js 的 StreamWordFind |
| 深拷贝 | 递归前登记副本,解决循环引用和共享引用。 | 支持普通对象、数组、日期、正则、Map、Set 和二进制视图;函数保持引用 | src/deepCopy.js、src/ts/deepCopy.ts |
| 原型链判断 | 逐级检查 prototype;原始值不是实例。 | 右侧必须是有效构造函数 | src/js/instanceof.js |
异步与时间控制
| 算法 | 关键知识点 | 失败或边界行为 | 源码 |
|---|---|---|---|
| Promise.all | 结果按输入位置排列,与完成顺序无关。 | 任一拒绝则整体拒绝;空列表得到 [] | src/promiseAll.js |
| 并发任务队列 | 同时运行数不超过上限,完成一个再启动下一个;结果按加入顺序排列。 | 失败也释放槽位,空闲后向调用者报错 | src/js/concurrentReq.js、src/js/reqControl.js、src/ts/reqControl.ts |
| 防抖 | 最后一次调用重置计时,安静期后执行。 | 保留最后一次的参数和 this | src/js/throttle-debounce.js、src/ts/debounce.ts |
| 立即防抖 | 首次立即执行,窗口内的新调用合并为尾沿一次。 | 尾沿使用最后一次的参数 | src/js/throttle-debounce.js 的 debounceImmediate |
| 前沿节流 | 每个时间窗口只执行首次调用。 | 窗口内其他调用跳过 | src/js/throttle-debounce.js、src/ts/throttle.ts |
| 定时器节流 | 首次调用安排延后执行。 | 等待期间的调用跳过 | src/js/throttle-debounce.js 的 throttleTimer |
| 尾沿节流 | 窗口末尾使用最后一次调用,保证持续触发时最后的值可见。 | 可关闭尾沿;使用最后的参数和 this | src/js/throttle-debounce.js 的 throttleTrailing |
| 漂移补偿定时器 | 按计划时间安排下一次触发,错过的周期直接跳过。 | 返回取消函数,防止定时器继续运行 | src/js/timer.js |
函数组合与事件
| 算法 | 关键知识点 | 源码 |
|---|---|---|
| 柯里化与分批求和 | 每条部分调用链独立保存参数,避免不同调用相互污染。 | src/js/curry.js、src/js/curry2.js、src/js/curry3.js |
| 函数组合 | 从右向左执行;没有函数时直接返回初始值。 | src/js/curry4.js 的 compose |
| 事件总线 | once 回调执行前先取消订阅,递归触发也只运行一次。 | src/js/eventBus.js |
| call、apply、bind、new | 参数传递、绑定 this 与构造调用是不同语义;这里用 Reflect 和原生 bind 保留完整构造行为。 | src/js/_apply_call_new.js、src/ts/call.ts、src/ts/apply.ts、src/ts/bindNew.ts、src/ts/new.ts |
| 模块加载 | 模块先进入缓存,再执行工厂,循环依赖才能读到部分导出;加载失败会移除缓存。 | src/js/imitateLoad.js 的 createModuleLoader |
practice/algorithms.ts 仍是留给自己动手的练习区;它的四个空实现不会影响上面这些参考实现。