力扣刷了就忘?我开发了一个帮你规划复习时间的网站

0 阅读4分钟

前言

作为一名开发人员,想进到好公司刷题是必不可少的。但是在我断断续续刷了几个月的力扣后发现,力扣官方并不能解决我最想解决的痛点问题 —— 我刷完就忘。

很多时候刷完一道题,记录在 Markdown 里,就几乎再也没打开过。等面试前想起来复习,发现当初的思路早就忘得干干净净,只能从头开始。

作为一名前端,自然想通过自己的方式解决这个问题。最近待业在家,于是闲着无聊跟 AI 一起开发了一个刷题记录平台,也算是对自己最近生活的一个记录吧。

痛点问题

在开发之前,主要定位了几个我发现的痛点问题:

  1. 刷题完全靠心情,对部分题高频遗忘,也没有提醒功能。
  2. 笔记工具打开太麻烦,自己用 Obsidian,每次还得切来切去。
  3. 力扣或者其它刷题平台没有掌握程度的记录,只能凭自己的印象判断哪些题掌握的比较好。

技术栈

  • 前端:Next.jsZustandTailwindFramer Motionshadcn
  • 数据库:PrismaSQLite
  • 编辑器: @monaco-editor/react
  • 内容解析: React MarkdownKaTeX

网站特点

  1. 支持 Markdown 写笔记,双栏实时预览,支持 LaTeX 公式渲染。
  2. 支持多种编程语言的代码记录、代码高亮、自动补全,Monaco Editor 是 VSCode 同款内核。
  3. 根据对题目的掌握程度自动计算下次复习时间。
  4. 网站由本地部署,数据库存储在本地文件中。备份、迁移都很方便。
  5. 支持中英双语切换。
  6. 支持力扣国际站的自动生成题目 slug 跳转链接。(准确率应该在80%/90%以上)

home.png

questions.png

review.png

如何解决复习问题?

使用的算法

参考了著名的 SM-2 算法

因为绝大多数开源复习软件都是基于这个逻辑,该算法的核心就是解决根据反馈,找到下一次复习的最佳时间点的问题的。

我微微简化并修改了算法,现在你只需要在官方平台提交解题代码后,根据自己的掌握程度给出一个自评分数(0-5),系统就会动态计算下一次复习的最佳时间。

简述一下算法逻辑

变量对应关系

变量名术语作用
Mastery Level (0-5)q (Quality)自己对该知识点掌握程度的评分
Easiness (E-Factor)EF衡量题目难度的动态指标
Interval (I)I (Interval)两次复习之间的间隔天数
Review Countn该题目的成功复习次数

论文中定义 EFEF 的更新公式如下:

EF=f(EF,q)=EF+(0.1(5q)×(0.08+(5q)×0.02))EF' = f(EF, q) = EF + (0.1 - (5 - q) \times (0.08 + (5 - q) \times 0.02))

  • EFEF':更新后的易读因子。

该公式设定了 EFEF 的下限为 1.3。所以如果因子低于这个值,则会认为题目过于困难,缩短复习间隔。

复习间隔递增逻辑

  • n=1n=1(第一次复习):I=1I = 1

  • n=2n=2(第二次复习):I=6I = 6

  • n>2n>2I(n)=I(n1)×EFI(n) = I(n-1) \times EF

核心代码解剖

差异化初始易读因子

这部分跟论文原文有所出入,SM-2 算法的标准起始值是 2.5,但是因为并不是所有题目的难度都一样,所以根据力扣的官方难度,为每道题赋予了不同的初始 EFEF 值:

const getInitialEasiness = (diff: string) => {
  if (diff === "Hard") return 2.4;
  if (diff === "Easy") return 2.6;
  return 2.5;
};

防止复习数量的滚雪球

如果你某一天突然刷了 50 道题(尽管这几率确实很小),那么按照计算,几天后这 50 道题会同时出现在你的复习列表里,造成崩盘。所以我使用了随机抖动策略,尽量将复习时间分散一下。

// 对 10 天以上的间隔进行 ±5% 的随机抖动
if (nextInterval > 10) {
  const fuzz = 0.95 + Math.random() * 0.1;
  nextInterval = Math.ceil(nextInterval * fuzz);
}

毕业机制

刷某道题不应该是永无止境的,所以我设置了一个 GRADUATION_INTERVAL,当复习间隔长达一年时,说明该题目已进入长时记忆,自动将其标记为 Mastered

if (nextInterval >= SRS_CONFIG.GRADUATION_INTERVAL) {
  status = "Mastered"; // 表示你已经完全掌握了
  nextInterval = SRS_CONFIG.GRADUATION_INTERVAL;
}

写在最后

其实这个本地网站的名字叫 ReCode,灵感来自于 Review 和 LeetCode,又有点谐音的意思,所以就叫这个了。

ReCode 是我为了提高自己学习效率而诞生的项目,可能存在或多或少的问题,还请见谅。如果它能帮你少走一些弯路,或者在面试前多一份从容,那它的意义就达到了。

目前已在 GitHub 开源,支持 Windows/Mac 一键脚本启动。

仓库链接:ReCode