mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-03 13:27:36 +00:00
Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2835f618aa | ||
|
|
7249da8bd6 | ||
|
|
83cb284e87 | ||
|
|
f33ded37ec | ||
|
|
ddf5dc483c | ||
|
|
e3d915513d | ||
|
|
f6c580b14b | ||
|
|
c753937852 | ||
|
|
8e07899157 | ||
|
|
886024c4f0 | ||
|
|
37a2d39269 | ||
|
|
a7ab989d57 | ||
|
|
0ccd9794f0 | ||
|
|
b922205bd0 | ||
|
|
def494d524 | ||
|
|
0a4b8d8b49 | ||
|
|
f1a952f2bd | ||
|
|
ea44a99c40 | ||
|
|
00fa322a14 | ||
|
|
bf56e170a4 | ||
|
|
4cf976f683 | ||
|
|
ace1944786 | ||
|
|
13734e2e27 | ||
|
|
cb4a03b6fa | ||
|
|
e14432d32a | ||
|
|
8843cb776c | ||
|
|
0e58b63d4d | ||
|
|
b5c3f1a159 | ||
|
|
37d3d9ceaf | ||
|
|
5627d0ca64 | ||
|
|
598688977a | ||
|
|
a3e846c606 | ||
|
|
52d9654ade | ||
|
|
f202c3a0a1 | ||
|
|
030887c7df | ||
|
|
733c71d364 | ||
|
|
115b901fbe |
No files matched your search
@@ -2,7 +2,7 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
LeetCode interview-prep campaign (2026-08-17 → Nov 3: eight weeks of topics, then the Review-issue phase; the end is the last date in `apps/api/data/schedule.json` — `CAMPAIGN_END`/`CAMPAIGN_DAYS` in `apps/api/src/srs.ts` are derived from it, never declared) run as a repo: solution files under `work/`, a Blume docs site under `apps/docs/`, GitHub Actions that reconcile issues and publish docs, and a Cloudflare Worker (`apps/api/`) that runs the spaced-repetition system (SRS) — daily digest email, one-tap logging, weekly review issues, live charts. Issues model the curriculum: ~24 `topic` issues (`Topic NN — Name`) own ~161 `problem` sub-issues titled `LC <num> · <Name>`, with difficulty and set carried **only** by labels (`set:core|optional|deferred`, `diff:*`) — never by the title; milestones are phases; a user-level GitHub Project ("Interview Prep", #2) mirrors review state.
|
||||
LeetCode interview-prep campaign (2026-08-17 → Dec 5: eight weeks of topics, the Review-issue phase, a two-week break, then a 30-day interview sprint from Fri Nov 6 (`SPRINT_START`); the end is the last date in `apps/api/data/schedule.json` — `CAMPAIGN_END`/`CAMPAIGN_DAYS`/`SPRINT_DAYS` in `apps/api/src/srs.ts` are derived from it, never declared) run as a repo: solution files under `work/`, a Blume docs site under `apps/docs/`, GitHub Actions that reconcile issues and publish docs, and a Cloudflare Worker (`apps/api/`) that runs the spaced-repetition system (SRS) — daily digest email, one-tap logging, weekly review issues, live charts. Issues model the curriculum: ~24 `topic` issues (`Topic NN — Name`) own ~161 `problem` sub-issues titled `LC <num> · <Name>`, with difficulty and set carried **only** by labels (`set:core|optional|deferred`, `diff:*`) — never by the title; milestones are phases; a user-level GitHub Project ("Interview Prep", #2) mirrors review state.
|
||||
|
||||
## Architecture & Data Flow
|
||||
|
||||
@@ -38,9 +38,9 @@ flowchart LR
|
||||
- **Determinism = idempotency.** Drill/gate sampling uses a seeded PRNG (`rng()` in `apps/api/src/srs.ts`, FNV-1a → mulberry32, seed = date / ISO week); the digest is keyed by ET date in `email_log`; one-tap links are unique on (problem, date, kind).
|
||||
- **DST-proof crons:** each event has two UTC crons; code fires only on the computed ET hour (`etHour`), unit-checked in `apps/api/src/srs.test.ts`.
|
||||
- **Chained workflows:** `GITHUB_TOKEN`-driven issue closes fire no `issues` events, so `close-topics.yml` chains off `workflow_run` of Close Solved instead. The Worker's webhook uses its own `GH_PAT`, so its events flow normally.
|
||||
- **One "finished topic" rule, two consumers.** `apps/cli/topics.ts` (`coreSet()`: every `set:core` sub-issue closed, zero core = never) is read by `close-topics.ts` (closes the topic issue) and `release-topics.ts` (the `release` job of `close-solved.yml`). The release reads the sub-issues, not the topic issue, so the push that finishes a topic releases it in the same run instead of waiting for Close Topics to chain after it. Version = [timever](https://gist.github.com/twolfson/de1b004dd22536b8e668) (`YYYYMMDD.HHMMSS.NNNNNNNNN`, UTC) of the instant the last core problem closed; the tag is annotated and backdated to that instant, on the last first-parent main commit before it; the body is a changelog snapshot at that instant (a one-sentence summary first, then the problems closed on the finish day / earlier, with solution links at the tag — problems still open at the finish are left out). A release is found by its `<!-- release-topics: #N -->` body marker, never by tag name, so re-closing a core problem never mints a second one; an existing release keeps its tag and has its name (the topic title) and body rewritten whenever the render changes. The docs `/changelog` is ONE custom page, `apps/docs/pages/changelog.astro` (a NeonUI-style timeline: date left, a card per release right, no per-release subpages, so nothing of it enters the sidebar or prev/next), linked from the header by `navigation.actions` (not a tab). `apps/docs/lib/releases.ts` fetches the releases at build (`GITHUB_TOKEN` in `deploy.yml`; a failed fetch fails the build), dates each by its timever tag, not `published_at` (which the API cannot backdate), tags it `Topic` or `Review` from the release name, and lists the rows of its `### Closed …` tables — so the tag format and that table shape are the page's contract with `render()`. There is no changelog RSS feed.
|
||||
- **One "finished topic" rule, two consumers.** `apps/cli/topics.ts` (`coreSet()`: every `set:core` sub-issue closed, zero core = never) is read by `close-topics.ts` (closes the topic issue) and `release-topics.ts` (the `release` job of `close-solved.yml`). The release reads the sub-issues, not the topic issue, so the push that finishes a topic releases it in the same run instead of waiting for Close Topics to chain after it. Version = [timever](https://gist.github.com/twolfson/de1b004dd22536b8e668) (`YYYYMMDD.HHMMSS.NNNNNNNNN`, UTC) of the instant the last core problem closed; the tag is annotated and backdated to that instant, on the last first-parent main commit before it; the body is a changelog snapshot at that instant (a one-sentence summary first, then the problems closed on the finish day / earlier, with solution links at the tag — problems still open at the finish are left out). A release is found by its `<!-- release-topics: #N -->` body marker, never by tag name, so re-closing a core problem never mints a second one; an existing release keeps its tag and has its name (the topic title) and body rewritten whenever the render changes. The docs `/changelog` is ONE custom page, `apps/docs/pages/changelog.astro` (a NeonUI-style timeline: date left, a card per release right, no per-release subpages, so nothing of it enters the sidebar or prev/next), linked from the header by a `navigation.tabs` entry (beside `/appendix`'s). `apps/docs/lib/releases.ts` fetches the releases at build (`GITHUB_TOKEN` in `deploy.yml`; a failed fetch fails the build), dates each by its timever tag, not `published_at` (which the API cannot backdate), tags it `Topic` or `Review` from the release name, and lists the rows of its `### Closed …` tables — so the tag format and that table shape are the page's contract with `render()`. There is no changelog RSS feed.
|
||||
- **Review issues re-parent problems; topics still own them.** The transfer phase's 20 `Review <N> — <Phase>` issues (label `review`, milestone `Review — Transfer & Mocks`; not the Worker's `Review — Week N` gate issues) hold open optional/deferred problems as sub-issues in solve order, moved there with `replace_parent`. `apps/api/src/reviews.ts` is the ONE recovery of ownership — `readReviews()` (one GraphQL request per 50 `review` issues, cheap enough for the Worker's cron subrequest budget) reads each moved problem's topic off its last `parent_issue_removed` event naming a topic, `movedFromTopics()` groups them — shared by the Worker's catalog reconcile (a moved problem's D1 row keeps syncing its labels and `topic_issue`) and `apps/cli/topics.ts` (`readProblems()` returns a topic's sub-issues plus its moved problems, so the finished rule and existing release bodies do not change when a problem moves). A Review issue finishes by `completion()` in `topics.ts` — every sub-issue closed, any set — which `close-topics.ts` closes it on and `release-topics.ts` releases it on, exactly like a topic: timever of the last close, same `<!-- release-topics: #N -->` marker (issue numbers never collide), body fields from the review's `**Scheduled:**`/`**Phase:**`/`**Load:**` lines.
|
||||
- **Review days are plan days.** `schedule.json` books each Review issue on its own date — Mon/Tue/Thu/Fri only, Oct 1 → Nov 3 — as `review: { issue, title, problems }` (solve order). The digest renders it as a linked "Today's review" section with the issue's day rules (`REVIEW_RULES` in `email.tsx`, one sentence for HTML and text), its problems join `RESERVED` so drills and sampled gates never draw them early, and `gate: []` skips a Saturday's checkpoint (Oct 3). The issue's sub-issues stay the truth: the catalog reconcile compares every booked review's title and problem list with GitHub and returns any mismatch as `reviewDrift` (also logged), so a re-arranged Review issue shows up the next sync instead of silently disagreeing with the email. Move a review by editing its `schedule.json` date AND the issue's `**Scheduled:**` line (the release body reads it).
|
||||
- **Review days are plan days.** `schedule.json` books each Review issue on its own date — Mon/Tue/Thu/Fri only; Reviews 1–6 ran Oct 1–9, Review 4 closed out Nov 2 after the break, and Reviews 7–20 run through the sprint (Nov 6 → Dec 1, Thanksgiving left unbooked), followed by two sweep days and a `gate: []` Saturday that marks the last day — as `review: { issue, title, problems }` (solve order). The digest renders it as a linked "Today's review" section with the issue's day rules (`REVIEW_RULES` in `email.tsx`, one sentence for HTML and text), its problems join `RESERVED` so drills and sampled gates never draw them early, and `gate: []` skips a Saturday's checkpoint (Oct 3, Dec 5). The issue's sub-issues stay the truth: the catalog reconcile compares every booked review's title and problem list with GitHub and returns any mismatch as `reviewDrift` (also logged), so a re-arranged Review issue shows up the next sync instead of silently disagreeing with the email. Move a review by editing its `schedule.json` date AND the issue's `**Scheduled:**` line (the release body reads it).
|
||||
|
||||
## Key Directories
|
||||
|
||||
@@ -48,7 +48,7 @@ flowchart LR
|
||||
|---|---|
|
||||
| `work/<1\|3\|7>/<Difficulty>/<Category>/<num>.<slug>.{js,py}` | Solutions, e.g. `work/1/Easy/Array/1.two-sum.py`. The top directory is the spaced-repetition bucket: `1` = first solve, `3`/`7` = the blind re-solves of that problem 3 and 7 days later. Machine-parsed header comment (title / `Difficulty:` / URL / `─` rule / statement) — preserve its exact shape |
|
||||
| `apps/cli/` | Flat Bun TS workspace: automation entries (shebang + top-level await) and libraries (no shebang, side-effect-free on import). Root `bun run` scripts delegate here |
|
||||
| `apps/docs/` | Blume site (`blume.config.ts`, `content/`, `pages/`, `components/` + `components.ts`, `islands/`, `public/`). `content/(0N-<phase>)/<topic>/(problems)/<num>_<slug>.mdx` plus one `meta.ts` per phase and per topic folder, all generated by sync (a phase's once, then hand-owned): the phase folder is a `(group)` (sidebar section, no route segment), the topic folder is the README campaign topic slug (`tree-dfs/`, `heap-priority-queue/`) and a real route segment, and `(problems)` is a second `(group)` inside it, so `tree-dfs/(problems)/226_invert-binary-tree.mdx` routes to `/tree-dfs/invert-binary-tree` (Blume sorts by the `<num>_` prefix and strips it from the route). The `meta.ts` files carry the exact labels, phase/topic numbers and icons so the sidebar reads in curriculum order; every topic meta is `display: "page"`, so a topic is one sidebar row whose panel holds its landing page and a "Problems" section. A problem page's badge row (between the description and the body) is rendered, not written: the MDX `wrapper` (`components/ProblemBadges.astro`, registered as `mdx.wrapper` in `components.ts` — Blume's page template renders the title and description itself with no slot before the body, so the wrapper, which MDX hands no route, reads it off `Astro.url`) reads the page's `topics:` (LeetCode's topic tags, from sync) and `video:` (NeetCode YouTube id, from `bun run videos`) frontmatter — both declared via `frontmatter.extend` in `blume.config.ts`, hand-rolled Standard Schemas, no zod — and links a tag the campaign teaches to its topic landing page (filled), any other tag to its card on the Appendix glossary `/appendix#<slug>` (outlined; LeetCode's problem list if the tag has no term, gray and iconless), and the video as a red `Explanation` badge. Every badge in the row is a pill. A tag badge carries the icon its target shows (a landing's: the topic `meta.ts` icon read off `data.navigation`; an Appendix term's: its `icon` in `appendix.ts`) and the tag's own colour from `lib/tag-colors.ts` — unique per tag site-wide, assigned greedily over every tag with a landing or a term so tags that share problem pages sit furthest apart in OKLab, in a lightness band that keeps ≥ 3:1 contrast in both themes (Blume's Badge takes one hex for both). The reverse direction is the `PageFooter` slot (`components/BacklinksList.astro`): an Obsidian-style `<num> <name>` list under every topic landing page of the problems whose badges link there, plus a topic's own folder members (so a topic no tag names, `hybrid-structures`, still lists its problems), in a native `<details>` open on load whose top-right chevron collapses it (no JS); problem pages are recognised by their `<num>. <name>` title and render no list. Which tags the campaign teaches is ONE map, `apps/docs/topic-landings.ts` (tag slug → landing routes), and where a badge points is ONE function beside it, `tagTarget()` — read by both components and the Appendix page (so badges, backlinks and term cards cannot disagree) and the map by sync. Every other tag a problem carries is ONE hand-written table, `apps/docs/appendix.ts` (`APPENDIX`: slug → `{ name, icon, category, definition }`), rendered by ONE custom page, `pages/appendix.astro` — a glossary with an A–Z letter index and a filter box (an `<appendix-filter>` custom element over static markup; no JS, the full list), one card per term (icon tinted the tag's colour, category, definition, LeetCode list link, and a `<details>` "Solved" list of the problem pages whose badge `tagTarget()` sends there, paged `PAGE_SIZE` (5) rows at a time by numbered buttons under it — a `<solved-pages>` custom element; without JS every row shows and the buttons stay hidden), each card `id`'d by slug and highlighted as `:target`. It is no content folder, so it enters neither the sidebar tree nor prev/next: `navigation.featured` pins it above the tree. Sync writes nothing for it — it warns about a carried tag with no term, a term whose tag a topic now teaches (delete it), and a term `icon` that is a `TOPIC_ICONS` value or another term's. The former per-term pages `/appendix/<slug>` are exact redirects to `/appendix#<slug>` in `blume.config.ts`, generated from `APPENDIX`'s keys. A hand-written `(0N-<phase>)/<topic>/index.mdx` (`hidden: true`) is that topic's landing page: Blume ≥ 1.7.3 links the sidebar group row to it (`sliding-window/index.mdx` → `/sliding-window`) and the hidden flag drops the duplicate row. Two kinds of custom MDX component, both import-free on every page: `islands/<Name>.tsx` is hydrated React (auto-discovered; the five `/progress` cards `SolvedRing`/`SubmissionHeatmap`/`SubmissionChart`/`TopicBubbles`/`RecentSolves`, and `DependencySpine` — the README topic map as data, zoom/pan, nodes link to whatever route Blume's `/api/docs/navigation.json` reports for the topic folder, inert until a topic has pages), `components/<Name>.astro` registered in `components.ts` is static markup plus aLine truncated
|
||||
| `apps/docs/` | Blume site (`blume.config.ts`, `content/`, `pages/`, `components/` + `components.ts`, `islands/`, `public/`). `content/(0N-<phase>)/<topic>/(problems)/<num>_<slug>.mdx` plus one `meta.ts` per phase and per topic folder, all generated by sync (a phase's once, then hand-owned): the phase folder is a `(group)` (sidebar section, no route segment), the topic folder is the README campaign topic slug (`tree-dfs/`, `heap-priority-queue/`) and a real route segment, and `(problems)` is a second `(group)` inside it, so `tree-dfs/(problems)/226_invert-binary-tree.mdx` routes to `/tree-dfs/invert-binary-tree` (Blume sorts by the `<num>_` prefix and strips it from the route). The `meta.ts` files carry the exact labels, phase/topic numbers and icons so the sidebar reads in curriculum order; every topic meta is `display: "page"`, so a topic is one sidebar row whose panel holds its landing page and a "Problems" section. A problem page's badge row (between the description and the body) is rendered, not written: the MDX `wrapper` (`components/ProblemBadges.astro`, registered as `mdx.wrapper` in `components.ts` — Blume's page template renders the title and description itself with no slot before the body, so the wrapper, which MDX hands no route, reads it off `Astro.url`) reads the page's `topics:` (LeetCode's topic tags, from sync) and `video:` (NeetCode YouTube id, from `bun run videos`) frontmatter — both declared via `frontmatter.extend` in `blume.config.ts`, hand-rolled Standard Schemas, no zod — and links a tag the campaign teaches to its topic landing page (filled), any other tag to its card on the Appendix glossary `/appendix#<slug>` (outlined; LeetCode's problem list if the tag has no term, gray and iconless), and the video as a red `Explanation` badge. Every badge in the row is a pill. A tag badge carries the icon its target shows (a landing's: the topic `meta.ts` icon read off `data.navigation`; an Appendix term's: its `icon` in `appendix.ts`) and the tag's own colour from `lib/tag-colors.ts` — unique per tag site-wide, assigned greedily over every tag with a landing or a term so tags that share problem pages sit furthest apart in OKLab, in a lightness band that keeps ≥ 3:1 contrast in both themes (Blume's Badge takes one hex for both). The reverse direction is the `PageFooter` slot (`components/BacklinksList.astro`): an Obsidian-style `<num> <name>` list under every topic landing page of the problems whose badges link there, plus a topic's own folder members (so a topic no tag names, `hybrid-structures`, still lists its problems), in a native `<details>` open on load whose top-right chevron collapses it (no JS); problem pages are recognised by their `<num>. <name>` title and render no list. The sidebar is Blume's default nav tree (no `Sidebar` slot override). The custom pages `/progress`, `/changelog` and `/appendix` are header `navigation.tabs` (on mobile, Blume lists them at the top of the drawer); never `navigation.featured`, which Blume renders above the nav tree. Which tags the campaign teaches is ONE map, `apps/docs/topic-landings.ts` (tag slug → landing routes), and where a badge points is ONE function beside it, `tagTarget()` — read by both components and the Appendix page (so badges, backlinks and term cards cannot disagree) and the map by sync. Every other tag a problem carries is ONE hand-written table, `apps/docs/appendix.ts` (`APPENDIX`: slug → `{ name, icon, category, definition }`), rendered by ONE custom page, `pages/appendix.astro` — a glossary with an A–Z letter index and a filter box (an `<appendix-filter>` custom element over static markup; no JS, the full list), one card per term (icon tinted the tag's colour, category, definition, LeetCode list link, and a `<details>` "Solved" list of the problem pages whose badge `tagTarget()` sends there, paged `PAGE_SIZE` (5) rows at a time by numbered buttons under it — a `<solved-pages>` custom element; without JS every row shows and the buttons stay hidden), each card `id`'d by slug and highlighted as `:target`. It is no content folder, so it enters neither the sidebar tree nor prev/next: its header tab is its link. Sync writes nothing for it — it warns about a carried tag with no term, a term whose tag a topic now teaches (delete it), and a term `icon` that is a `TOPIC_ICONS` value or another term's. The former per-term pages `/appendix/<slug>` are exact redirects to `/appendix#<slug>` in `blume.config.ts`, generated from `APPENDIX`'s keys. A hand-written `(0N-<phase>)/<topic>/index.mdx` (`hidden: true`) is that topic's landing page: Blume ≥ 1.7.3 links the sidebar group row to it (`sliding-window/index.mdx` → `/sliding-window`) and the hidden flag drops the duplicate row. Two kinds of custom MDX component, both import-free on every page: `islands/<Name>.tsx` is hydrated React (auto-discovered; `DependencySpine` — the README topic map as data, zoom/pan, nodes link to whatever route Blume's `/api/docs/navigaLine truncated
|
||||
| `apps/api/` | Cloudflare Worker workspace: `src/` modules, `data/schedule.json` (the plan: `date → { topics | problems | review | gate | sweep }`, human-edited, bundled at deploy), `migrations/` (append-only; `0002` is the +3/+7 ladder rebuild) |
|
||||
| `.github/workflows/` | deploy (docs → Pages with `GITHUB_TOKEN` for the releases-backed changelog, Worker → Cloudflare with the `OPENROUTER_API_KEY` Actions secret put into it first, on every main push), close-solved (issues + `/admin/solved` + Project `Set`/`Difficulty`, needs `SRS_ADMIN_KEY` and `PROJECT_PAT`; then the `release` job: timever tag + release per finished topic, `contents: write`), close-topics, sync-d1 (issue edits → `/admin/reconcile`), spaced-repetition (daily 06:00 ET cron → today's review issue; `GITHUB_TOKEN` only, no secrets) |
|
||||
| `.githooks/` | Tracked git hooks; `bun install` points `core.hooksPath` here (root `prepare` script, no husky). `pre-commit` runs `bun run sync` and stages only what sync changed (pages, `apps/docs/redirects.ts`), so a solution, its page and its `/<num>` short link land in one commit; it refuses to run while `work/1` has anything unstaged (sync reads the tree, so the page would not match the commit), and leaves pre-existing dirt under `apps/docs/content` (in-progress `## Approach` prose) unstaged. `git commit --no-verify` for a deliberate partial commit |
|
||||
@@ -68,9 +68,14 @@ bun run stats # terminal dashboard over GET /api/stats — four panes
|
||||
# r refetches, q quits. Outside a TTY it prints every pane once.
|
||||
# SRS_API points it at a local Worker like close-solved does.
|
||||
bun run test # run ONE solution against LeetCode's judge (not a test suite;
|
||||
# most recently tested first, stamped in the db on exit 0)
|
||||
bun run submit # submit ONE solution to LeetCode (newest scaffold first, nothing
|
||||
# hidden; last_submitted stamped in the db on exit 0)
|
||||
# files git sees as modified/untracked first, so Enter runs the
|
||||
# one in progress; then most recently tested; stamped on exit 0)
|
||||
bun run submit # submit ONE solution to LeetCode (uncommitted first, then newest
|
||||
# scaffold, nothing hidden; last_submitted stamped on exit 0).
|
||||
# Both fold in the signed-in profile's submission history once
|
||||
# a day (leetcode-cli's keychain login; GraphQL only — LeetCode's
|
||||
# REST /api/* answers 403), so a lost db or fresh clone recovers
|
||||
# both orderings
|
||||
bun run sync # work/1 → apps/docs/content/ pages + apps/docs/redirects.ts (/<num> short
|
||||
# links; re-solves are never published)
|
||||
bun run videos # docs pages with no `video:` frontmatter → NeetCode video per page
|
||||
@@ -108,15 +113,15 @@ curl -X POST -H "Authorization: Bearer $LINK_KEY" \
|
||||
|
||||
## Important Files
|
||||
|
||||
- `apps/api/src/srs.ts` — SRS domain: the ladder (`new → +3 → +7 → retired`; fail resets to `+3`; first-ever log enters at `+3`), `WINDOWS`/`STAGES`/`TEMPERATURES`/`DIFFICULTIES`/`SETS` (the one vocabulary shared by the charts, the `work/<n>` buckets, the review issues and the stats dashboard), ET date math, `workingDay()` (no scheduled date is ever a Sunday), `temperatureOf()` (days since a concept was last exercised → `hot`/`fresh`/`fading`/`cold`, bands derived from `WINDOWS` so they cannot drift; read-side only, nothing is scheduled off it), seeded sampling, `logAttempt()` (the ONE write path — email taps, webhook, gate scoring all converge here), and overload leveling: at most `REVIEW_CAP` (3) reviews surface per day — `levelReviews()` (run by `sendDigest` on real sends only, never dry) gives everything past the cap a concrete future WORKING day, ≤ 3 per day, oldest first, instead of letting the due pile grow.
|
||||
- `apps/api/src/srs.ts` — SRS domain: the ladder (`new → +3 → +7 → retired`; fail resets to `+3`; first-ever log enters at `+3`), `WINDOWS`/`STAGES`/`TEMPERATURES`/`DIFFICULTIES`/`SETS` (the one vocabulary shared by the charts, the `work/<n>` buckets, the review issues and the stats dashboard), ET date math, `workingDay()` (no scheduled date is ever a Sunday), `temperatureOf()` (days since a concept was last exercised → `hot`/`fresh`/`fading`/`cold`, bands derived from `WINDOWS` so they cannot drift; read-side only, nothing is scheduled off it), seeded sampling, `logAttempt()` (the ONE write path — email taps, webhook, gate scoring all converge here), and overload leveling: at most `REVIEW_CAP` (5 since the break, to drain its backlog within the sprint) reviews surface per day — `levelReviews()` (run by `sendDigest` on real sends only, never dry) gives everything past the cap a concrete future WORKING day, ≤ 5 per day, oldest first, instead of letting the due pile grow.
|
||||
- `apps/api/src/index.ts` — router + cron dispatch; `apps/api/wrangler.jsonc` — bindings (`DB`, `EMAIL`), crons, vars; secrets `GH_PAT`/`WEBHOOK_SECRET`/`LINK_KEY` via `wrangler secret put`, and `OPENROUTER_API_KEY`, whose one copy is the repo's Actions secret (deploy.yml puts it before every deploy — rotate it there).
|
||||
- `apps/api/src/ask.ts` — `POST /ask`, the docs assistant's backend. Blume's own assistant route is server-rendered and cannot ship in the static Pages build (`BLUME_SERVER_FEATURE_REQUIRED`), so `blume.config.ts` sets `ai.assistant.endpoint` to this route instead of a `provider`. It speaks the Blume island's wire contract (`{ messages, page: { path } }` in, a plain-text stream out, a 503 "The assistant is not configured…" when the key is missing), validates like Blume's generated route (1–40 user/assistant messages, 24 000 chars, 64 KB — no caller system prompt), grounds on the published `llms-full.txt` (current page first, then keyword-ranked excerpts in Blume's 6 × 2 000 / 10 000-char budget, cached 5 min), and streams OpenRouter's chat completions over plain `fetch` — no AI SDK — for the `ASK_MODEL` var. `:free` models share an upstream pool and often answer 429, which the island shows as its rate-limit notice.
|
||||
- `apps/api/src/digest.ts` / `apps/api/src/email.tsx` — the daily digest, split data/presentation. `digest.ts` reads D1 into a `DigestData`; `email.tsx` owns every colour and every sentence, and renders both the HTML and (via its own `plainDigest`, not React Email's `plainText` mode, which flattens the tables) the text alternative. The retrieval rules hold by construction: `DigestRow` has no title and no issue field, so a review or drill line *cannot* leak the topic or a solution link. Subject is `(Day N/<CAMPAIGN_DAYS>) LeetCode Daily Digest` — 79 with the plan ending Nov 3; booking a later day in `schedule.json` lengthens it, and the heatmaps and `/api/stats` `heat` with it.
|
||||
- `apps/api/src/digest.ts` / `apps/api/src/email.tsx` — the daily digest, split data/presentation. `digest.ts` reads D1 into a `DigestData`; `email.tsx` owns every colour and every sentence, and renders both the HTML and (via its own `plainDigest`, not React Email's `plainText` mode, which flattens the tables) the text alternative. The retrieval rules hold by construction: `DigestRow` has no title and no issue field, so a review or drill line *cannot* leak the topic or a solution link. Subject is `(Day N/<SPRINT_DAYS>) LeetCode Daily Digest` and the progress line `Day N of <SPRINT_DAYS> · Week k` (Mon–Sun weeks, so the Friday start makes week 1 three days long) — 30 with the sprint running Nov 6 → Dec 5; before day 1 they count down (`(Sprint in N days)`). Booking a later day in `schedule.json` lengthens the sprint, and the whole-campaign heatmaps and `/api/stats` `heat` (still from `CAMPAIGN_START`) with it.
|
||||
- `apps/api/src/png.ts` — hand-rolled PNG encoder (RGB8, one IDAT, zlib via `CompressionStream("deflate")`, CRC32, 5×7 bitmap font). It exists because every major email client refuses remote SVG, so `/chart/heatmap.png` rasters the heatmap for the digest while `/chart/heatmap.svg` keeps serving the README byte-for-byte. Both come from one `heatmapCells()` so the two pictures cannot drift.
|
||||
- `apps/cli/sync.ts` — work→docs contract: on existing pages only the frontmatter `icon:` and the `## Solution` section are regenerated, and `topics:` (LeetCode's `topicTags` as `{ name, slug }`, fetched by GraphQL) and `issue:` (the URL of the problem's `problem`-labelled GitHub issue, `LC <num> · …`, listed via `github()` — rendered as the neutral `Issue #N` badge after `Explanation`) are filled once — on create, or on any run that finds them missing — never refreshed (delete a key to refetch; offline or without `gh` auth, the page is written without it and the next sync retries; a sync with nothing missing makes no request). Only a problem the README plan lists gets `issue:` — an unplanned warm-up has no catalog issue, so it is never looked up. `## Solution` is the latter bounded by `solutionEnd()` at the next unfenced top-level heading; human prose before it and any sections after it are never touched. Never hand-write solution pages or edit inside `## Solution`. Two classifications, two homes: the sidebar group is the README topic (`readmeTopics()`, first listing wins for a problem under two topics; a warm-up outside the plan groups under its category at `order: 0`, ahead of topic 1) and the `work/1/<Difficulty>/<Category>/` category, which only places an unplanned problem — the page's topic badges are LeetCode's tags, not either classification. Topics nest under their README phase (`## Phase N — Name` headings, shortened via `PHASE_ALIASES`): `content/(0N-<phase>)/<topic>/`, with every problem page in the topic's `(problems)/` group as `<num>_<slug>.mdx` (a page is found by its frontmatter title, wherever it sits, so a layout change is one sync of `rename`s). The topic `meta.ts` is script-owned and rewritten every run (label, curriculum order, `TOPIC_ICONS` icon, `display: "page"`); the phase `meta.ts` is written once when missing (`Phase N: Name`, `order: N`) and hand-owned after — rename its title freely. Never rewriting it loses nothing: its order and name are also the folder name, so a README change to either lands in a new folder with a fresh meta. The phase folder is a Blume `(group)` — no route segment, so `/tree-dfs` is unchanged and a landing page there is one level deep, never at `/` — and the topic folder is the real segment; never put a topic `index.mdx` directly inside a `(group)`. A page is `rename`d when its topic or phase changes; a folder every page has left is deleted (climbing through an emptied `(problems)` and topic into an emptied phase folder) unless it still holds a hand-written `index.mdx` (sync never writes, moves, or deletes one — so page-relative asset paths in a moved landing page are yours to fix). Slugs come from `slugify()`, which glues a leading number to the next token (`1d-dynamic-programming`) because Blume strips `<digits>-` prefixes from routes. The hand-owned parts have their own owners: `## Approach` (a Mermaid flowchart tracing the page's code, placed *before* `## Solution` so a re-sync keeps it) is written per the `problem-approach` skill (`/problem-approach` fills every page missing one); the frontmatter `video:` (the NeetCode walkthrough, rendered as the red `Explanation` badge) is added by `bun run videos --write` (`apps/cli/videos.ts`).
|
||||
- `apps/cli/sync.ts` — work→docs contract: on existing pages only the frontmatter `icon:` and the `## Solution` section are regenerated, and `topics:` (LeetCode's `topicTags` as `{ name, slug }`, fetched by GraphQL) and `issue:` (the URL of the problem's `problem`-labelled GitHub issue, `LC <num> · …`, listed via `github()` — rendered as the neutral `Issue #N` badge after `Explanation`) are filled once — on create, or on any run that finds them missing — never refreshed (delete a key to refetch; offline or without `gh` auth, the page is written without it and the next sync retries; a sync with nothing missing makes no request). Only a problem the README plan lists gets `issue:` — an unplanned warm-up has no catalog issue, so it is never looked up. `companies:` (`{ name, period, frequency }`: every company that asked the problem in the last six months, at the most recent of the `Thirty Days`/`Three Months`/`Six Months` windows it appears in and its frequency there, most recent first — the vocabulary is ONE module, `apps/docs/companies.ts`, shared by sync, the schema and both readers) is the exception that IS rewritten every run, since recency ages: it comes from the local DuckDB company-frequency database (`~/.local/share/duckdb/data/leetcode-company-frequency.db`, built by that project's `mise run lc:build`) via one `duckdb -readonly -json` query, and where the database or the `duckdb` CLI is missing (CI) the step is skipped and pages keep the key as it is. `ProblemBadges.astro` renders it as a TypeTable-style table under the badge row (company pill · asked within · frequency bar), each pill linking to `companyHref()` — the company's Appendix card, filed A–Z among the glossary terms (category `Company`, no definition, no hand-written table: derived from every page's `companies:`), whose Solved backlinks are the problems it asked, each with how recently. `## Solution` is the latter bounded by `solutionEnd()` at the next unfenced top-level heading; human prose before it and any sections after it are never touched. Never hand-write solution pages or edit inside `## Solution`. Two classifications, two homes: the sidebar group is the README topic (`readmeTopics()`, first listing wins for a problem under two topics; a warm-up outside the plan groups under its category at `order: 0`, ahead of topic 1) and the `work/1/<Difficulty>/<Category>/` category, which only places an unplanned problem — the page's topic badges are LeetCode's tags, not either classification. Topics nest under their README phase (`## Phase N — Name` headings, shortened via `PHASE_ALIASES`): `content/(0N-<phase>)/<topic>/`, with every problem page in the topic's `(problems)/` group as `<num>_<slug>.mdx` (a page is found by its frontmatter title, wherever it sits, so a layout change is one sync of `rename`s). The topic `meta.ts` is script-owned and rewritten every run (label, curriculum order, `TOPIC_ICONS` icon, `display: "page"`); the phase `meta.ts` is written once when missing (`Phase N: Name`, `order: N`) and hand-owned after — rename its title freely. Never rewriting it loses nothing: its order and name are also the folder name, so a README change to either lands in a new folder with a fresh meta. The phase folder is a Blume `(group)` — no route segment, so `/tree-dfs` is unchanged and a landing page there is one level deep, never at `/` — and the topic folder is the real segment; never put a topic `index.mdx` directly inside a `(group)`. A page is `rename`d when its topic or phase changes; a folder every page has left is deleted (climbing through an emptied `(problems)` and topic into an emptied phase folder) unless it still holds a hand-written `index.mdx` (sync never writes, moves, or deletes one — so page-relative asset paths in a moved landing page are yours to fix). Slugs come from `slugify()`, which glues a leading number to the next token (`1d-dynamic-programming`) because Blume strips `<digits>-` prefixes from routes. The hand-owned parts have their own owners: `## Approach` (a Mermaid flowchart tracing the page's code, placed *before* `## Solution` so a re-sync keeps it) is written per the `problem-approach` skill (`/problem-approach` fills every page missing one); the frontmatter `video:` (the NeetCode walkthrough, rendered as the red `Explanation` badge) is added by `bun run videos --write` (`apps/cli/videos.ts`).
|
||||
- `apps/cli/work.ts` — the `work/` layout: bucket paths, `isSolution()`/`solvedInBucket()` (the stub test, shared with `close-solved.ts`), `firstSolved()` (one `git log`, ET dates, keyed by LC number so past layout moves don't matter), and `reviewQueues()` (what each window still owes). Everything that needs to know where a solution file lives, or when it was solved, goes through here.
|
||||
- `apps/api/src/stats.ts` / `apps/cli/stats.ts` / `apps/cli/tui.ts` / `apps/cli/api.ts` / `apps/docs/lib/stats.ts` — the read side. `buildStats()` is the ONE aggregation: phases, ladder, difficulty, `attempting` (open misses, via `MISS_WHERE` in `srs.ts` — the sweep day's definition), per-topic temperature, gates, the 14-day due queue, `heat` (every campaign day, zeros and future days included — the TUI windows its tail rather than asking a second question), `recent` (the ten problems most recently solved, dated by each one's first pass) and `year` (the 365 days ending today — always the calendar year to date — each split by difficulty into attempts and first solves; the docs cards window and bucket it themselves). Two readers consume it, the docs `/progress` islands and `bun run stats`, so its shape is their contract: change the Worker and both readers together (additive fields a reader ignores are safe). `tui.ts` is hand-rolled ANSI (bars, sparklines, heat cells, alternate screen) on charts.ts's palette, and every chart differs by GLYPH as well as by colour so a piped or `NO_COLOR` dashboard still reads. The TUI recomputes nothing: the only local fact it shows is the `work/` solution count, which is inventory, not schedule.
|
||||
- `README.md` live charts are Worker endpoints (`/chart/*.svg`, `/badge/gate.svg`, 5-min Camo cache). The docs `/progress` page is five LeetCode-style islands — `SolvedRing` (difficulty gauge + "Attempting"), `SubmissionHeatmap` and `SubmissionChart` sharing one row (the heatmap: the last six months in month blocks, the README's colour ramp as theme.css `--heat-0..4`, its attempts / active days / max streak counted from the cells it draws; the chart: this calendar year as stacked Easy/Med./Hard bars per month or week, first solves or every attempt, via two shadcn `ToggleGroup`s), `TopicBubbles` (the campaign topics circle-packed by `d3-hierarchy`, filled by solved share), `RecentSolves` (the ten most recent, linked through `redirects.ts`) — composed from shadcn/ui (`components/ui/{card,chart,skeleton,toggle,toggle-group}.tsx`, Recharts under `chart`, Radix under the toggles) on Blume's tokens: `apps/docs/theme.css` maps shadcn's names onto `--blume-*` (never `accent`, which is Blume's brand colour — vendored components that use it, like `toggle.tsx`, are retokened to `background`/`muted`) and gives shadcn's `data-slot` parts their border colour. They share `lib/stats.ts` (`useStats()`), so the page makes ONE `/api/stats` request however many islands hydrate. shadcn sources live outside `islands/` (every `.tsx` there becomes an island) and import `lib/utils` relatively; `components.json` + `tsconfig.json` exist for the shadcn CLI and `tsc`, so after `shadcn add` rewrite `@/…` imports to relative ones (house style) — and check `package.json`: the CLI has twice installed an unrelated `cn` package for `lib/utils` and skipped real deps (`class-variance-authority`). Blume turns every tsconfig `paths` target into a Vite alias AND a dev dep-scan glob (`<target>/**/*.astro`…), so `paths` maps only `@/components`, `@/lib`, `@/hooks` — each bare and `/*`, since `components.json` names the bare forms — never `@/*` → `./*`: that glob reaches through `node_modules/blume`, where Vite's scanner reads a `<script>` inside a `RootLayout.astro` comment as a tag and `blume dev` skips dependency pre-bundling. Blume's `.prose :where(a, p, ol)` rules ignore `not-prose`, so island markup overrides them with `!` utilities, as Blume's own components do.
|
||||
- `README.md` live charts are Worker endpoints (`/chart/*.svg`, `/badge/gate.svg`, 5-min Camo cache). The docs `/progress` page is a custom page, `apps/docs/pages/progress.astro` (no content folder, so outside the sidebar and prev/next; its header tab is its link), mounting five LeetCode-style React cards from `components/progress/` with `client:load` — imported by the page, never used from MDX, so they are not in `islands/` — `SolvedRing` (difficulty gauge + "Attempting"), `SubmissionHeatmap` and `SubmissionChart` sharing one row (the heatmap: the last six months in month blocks, the README's colour ramp as theme.css `--heat-0..4`, its attempts / active days / max streak counted from the cells it draws; the chart: this calendar year as stacked Easy/Med./Hard bars per month or week, first solves or every attempt, via two shadcn `ToggleGroup`s), `TopicBubbles` (the campaign topics circle-packed by `d3-hierarchy`, filled by solved share), `RecentSolves` (the ten most recent, linked through `redirects.ts`) — composed from shadcn/ui (`components/ui/{card,chart,skeleton,toggle,toggle-group}.tsx`, Recharts under `chart`, Radix under the toggles) on Blume's tokens: `apps/docs/theme.css` maps shadcn's names onto `--blume-*` (never `accent`, which is Blume's brand colour — vendored components that use it, like `toggle.tsx`, are retokened to `background`/`muted`) and gives shadcn's `data-slot` parts their border colour. They share `lib/stats.ts` (`useStats()`), so the page makes ONE `/api/stats` request however many cards hydrate. shadcn sources live outside `islands/` (every `.tsx` there becomes an island) and import `lib/utils` relatively; `components.json` + `tsconfig.json` exist for the shadcn CLI and `tsc`, so after `shadcn add` rewrite `@/…` imports to relative ones (house style) — and check `package.json`: the CLI has twice installed an unrelated `cn` package for `lib/utils` and skipped real deps (`class-variance-authority`). Blume turns every tsconfig `paths` target into a Vite alias AND a dev dep-scan glob (`<target>/**/*.astro`…), so `paths` maps only `@/components`, `@/lib`, `@/hooks` — each bare and `/*`, since `components.json` names the bare forms — never `@/*` → `./*`: that glob reaches through `node_modules/blume`, where Vite's scanner reads a `<script>` inside a `RootLayout.astro` comment as a tag and `blume dev` skips dependency pre-bundling. Blume's `.prose :where(a, p, ol)` rules ignore `not-prose`, so island markup overrides them with `!` utilities, as Blume's own components do.
|
||||
- `apps/docs/blume.config.ts` — site base `/leetcode`; imports `apps/docs/redirects.ts`, sync-generated (`/<num>` → `/<topic>/<slug>`, one row per page, exact root-relative paths — Blume adds the base and, for the static Pages build, emits a meta-refresh page per rule plus `blume-redirects.json`); `README.md` campaign table doubles as topic-map input to `sync.ts` (`readmeTopics()`, `TOPIC_ALIASES`, `PHASE_ALIASES`).
|
||||
- Old `.github/srs/` JSON state, its `srs-*` Actions, and the one-shot `import-srs.ts` importer are RETIRED and deleted — do not resurrect; git history is the record.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<sub>Interview-prep fight camp: eight weeks of topics, then a review phase to Nov 3. Each phase is a milestone; each topic an issue; each problem a sub-issue.<br>
|
||||
<sub>Interview-prep fight camp: eight weeks of topics, a review phase, then a 30-day interview sprint to Dec 5. Each phase is a milestone; each topic an issue; each problem a sub-issue.<br>
|
||||
New topic Mon–Fri, gate on Saturday, Sunday off — every solve comes back at <strong>+3</strong> and <strong>+7</strong> days,
|
||||
the two windows that keep the rest day clear (a Thursday solve's +3 slides to Monday).<br>
|
||||
Charts are live from the SRS Worker, never committed: pushing a solution or tapping the daily digest moves them within ~5 min.</sub>
|
||||
|
||||
+18
-15
@@ -26,21 +26,24 @@
|
||||
"2026-10-02": { "review": { "issue": 226, "title": "Review 2 — Linear Structures", "problems": [290, 525, 189, 1004, 84] }, "sweep": true },
|
||||
"2026-10-03": { "gate": [] },
|
||||
"2026-10-05": { "review": { "issue": 227, "title": "Review 3 — Linear Structures", "problems": [643, 128, 162, 71, 4] } },
|
||||
"2026-10-06": { "review": { "issue": 228, "title": "Review 4 — Linear Structures", "problems": [496, 80, 901, 76, 224] } },
|
||||
"2026-10-08": { "review": { "issue": 229, "title": "Review 5 — Nodal & Grid", "problems": [876, 19, 36] } },
|
||||
"2026-10-09": { "review": { "issue": 230, "title": "Review 6 — Nodal & Grid", "problems": [92, 82, 380] } },
|
||||
"2026-10-12": { "review": { "issue": 231, "title": "Review 7 — Nodal & Grid", "problems": [143, 86, 289] } },
|
||||
"2026-10-13": { "review": { "issue": 232, "title": "Review 8 — Nodal & Grid", "problems": [61, 138] } },
|
||||
"2026-10-15": { "review": { "issue": 233, "title": "Review 9 — Hierarchical", "problems": [100, 112, 530, 1046, 103, 124] } },
|
||||
"2026-10-16": { "review": { "issue": 234, "title": "Review 10 — Hierarchical", "problems": [572, 543, 703, 105, 295] } },
|
||||
"2026-10-19": { "review": { "issue": 235, "title": "Review 11 — Hierarchical", "problems": [101, 637, 173, 621, 502] } },
|
||||
"2026-10-20": { "review": { "issue": 236, "title": "Review 12 — Hierarchical", "problems": [108, 129, 373, 648, 212] } },
|
||||
"2026-10-22": { "review": { "issue": 237, "title": "Review 13 — Relational", "problems": [1971, 695, 547, 433] } },
|
||||
"2026-10-23": { "review": { "issue": 238, "title": "Review 14 — Relational", "problems": [130, 261, 909, 1462] } },
|
||||
"2026-10-26": { "review": { "issue": 239, "title": "Review 15 — Relational", "problems": [286, 1254, 721, 399] } },
|
||||
"2026-10-27": { "review": { "issue": 240, "title": "Review 16 — Relational", "problems": [1091, 802, 269] } },
|
||||
"2026-10-29": { "review": { "issue": 241, "title": "Review 17 — Decision Space", "problems": [746, 228, 17, 77, 213, 63, 122] } },
|
||||
"2026-10-30": { "review": { "issue": 242, "title": "Review 18 — Decision Space", "problems": [22, 90, 139, 120, 45, 253, 72] } },
|
||||
"2026-11-02": { "review": { "issue": 243, "title": "Review 19 — Decision Space", "problems": [79, 131, 91, 494, 763, 986, 920] } },
|
||||
"2026-11-03": { "review": { "issue": 244, "title": "Review 20 — Decision Space", "problems": [300, 647, 97, 221, 918, 452, 52] } }
|
||||
"2026-11-02": { "review": { "issue": 228, "title": "Review 4 — Linear Structures", "problems": [496, 80, 901, 76, 224] } },
|
||||
"2026-11-06": { "review": { "issue": 231, "title": "Review 7 — Nodal & Grid", "problems": [143, 86, 289] } },
|
||||
"2026-11-09": { "review": { "issue": 232, "title": "Review 8 — Nodal & Grid", "problems": [61, 138] } },
|
||||
"2026-11-10": { "review": { "issue": 233, "title": "Review 9 — Hierarchical", "problems": [100, 112, 530, 1046, 103, 124] } },
|
||||
"2026-11-12": { "review": { "issue": 234, "title": "Review 10 — Hierarchical", "problems": [572, 543, 703, 105, 295] } },
|
||||
"2026-11-13": { "review": { "issue": 235, "title": "Review 11 — Hierarchical", "problems": [101, 637, 173, 621, 502] } },
|
||||
"2026-11-16": { "review": { "issue": 236, "title": "Review 12 — Hierarchical", "problems": [108, 129, 373, 648, 212] } },
|
||||
"2026-11-17": { "review": { "issue": 237, "title": "Review 13 — Relational", "problems": [1971, 695, 547, 433] } },
|
||||
"2026-11-19": { "review": { "issue": 238, "title": "Review 14 — Relational", "problems": [130, 261, 909, 1462] } },
|
||||
"2026-11-20": { "review": { "issue": 239, "title": "Review 15 — Relational", "problems": [286, 1254, 721, 399] } },
|
||||
"2026-11-23": { "review": { "issue": 240, "title": "Review 16 — Relational", "problems": [1091, 802, 269] } },
|
||||
"2026-11-24": { "review": { "issue": 241, "title": "Review 17 — Decision Space", "problems": [746, 228, 17, 77, 213, 63, 122] } },
|
||||
"2026-11-27": { "review": { "issue": 242, "title": "Review 18 — Decision Space", "problems": [22, 90, 139, 120, 45, 253, 72] } },
|
||||
"2026-11-30": { "review": { "issue": 243, "title": "Review 19 — Decision Space", "problems": [79, 131, 91, 494, 763, 986, 920] } },
|
||||
"2026-12-01": { "review": { "issue": 244, "title": "Review 20 — Decision Space", "problems": [300, 647, 97, 221, 918, 452, 52] } },
|
||||
"2026-12-03": { "sweep": true },
|
||||
"2026-12-04": { "sweep": true },
|
||||
"2026-12-05": { "gate": [] }
|
||||
}
|
||||
+18
-6
@@ -10,12 +10,13 @@
|
||||
* either, so the rule holds by construction. The plan's own sections — a
|
||||
* learning day's topics (core, then the optional "if time" list), a set
|
||||
* day's problems, and a review day's `Review <N>` issue — are the only
|
||||
* labeled, linked ones. At most REVIEW_CAP (3)
|
||||
* labeled, linked ones. At most REVIEW_CAP (5)
|
||||
* reviews per day; drills fill toward DAILY_CAP (6) only when the due queue
|
||||
* is light, and never on a sweep day, which assigns every open miss and
|
||||
* nothing new. Overflow is levelled, not stacked: sendDigest gives every
|
||||
* review past the cap a concrete future date, ≤ REVIEW_CAP per day, oldest
|
||||
* first. Sunday is a two-line rest note.
|
||||
* first. Sunday is a two-line rest note. Day N/M counts the interview sprint
|
||||
* (SPRINT_START → the plan's last day); before it, the email counts down.
|
||||
*
|
||||
* Idempotency: email_log keys sends by ET date — a same-day re-send is a
|
||||
* no-op unless forced; the body itself is deterministic (drill picks are
|
||||
@@ -23,15 +24,15 @@
|
||||
* and a re-run finds nothing left over the cap.
|
||||
*/
|
||||
import {
|
||||
CAMPAIGN_DAYS,
|
||||
MISS_WHERE,
|
||||
type ProblemRow,
|
||||
RESERVED,
|
||||
REVIEW_CAP,
|
||||
SCHEDULE,
|
||||
SPRINT_DAYS,
|
||||
SPRINT_START,
|
||||
type ScheduledDay,
|
||||
addDays,
|
||||
campaignDay,
|
||||
campaignWeek,
|
||||
dueReviews,
|
||||
isoWeek,
|
||||
@@ -39,6 +40,8 @@ import {
|
||||
lookupProblems,
|
||||
pickDrills,
|
||||
prettyDate,
|
||||
sprintDay,
|
||||
sprintWeek,
|
||||
streak,
|
||||
weekdayOf,
|
||||
} from "./srs.ts";
|
||||
@@ -107,9 +110,13 @@ export async function collectDigest(env: Env, date: string): Promise<DigestData>
|
||||
const drills =
|
||||
rest || plan.sweep ? [] : await pickDrills(db, date, DAILY_CAP - Math.min(due.length, DAILY_CAP));
|
||||
|
||||
const sprint = sprintDay(date);
|
||||
const data: DigestData = {
|
||||
day: prettyDate(date),
|
||||
progress: `Day ${campaignDay(date)} of ${CAMPAIGN_DAYS} · Week ${campaignWeek(date)}`,
|
||||
progress:
|
||||
sprint >= 1
|
||||
? `Day ${sprint} of ${SPRINT_DAYS} · Week ${sprintWeek(date)}`
|
||||
: `The ${SPRINT_DAYS}-day interview sprint starts ${prettyDate(SPRINT_START)}`,
|
||||
streak: await streak(db, date),
|
||||
rest,
|
||||
topics: [],
|
||||
@@ -206,8 +213,13 @@ export async function collectDigest(env: Env, date: string): Promise<DigestData>
|
||||
export async function buildDigest(env: Env, date: string): Promise<Digest> {
|
||||
const data = await collectDigest(env, date);
|
||||
const { html, text } = await renderDigest(data);
|
||||
const sprint = sprintDay(date);
|
||||
const until = 1 - sprint;
|
||||
return {
|
||||
subject: `(Day ${campaignDay(date)}/${CAMPAIGN_DAYS}) LeetCode Daily Digest`,
|
||||
subject:
|
||||
sprint >= 1
|
||||
? `(Day ${sprint}/${SPRINT_DAYS}) LeetCode Daily Digest`
|
||||
: `(Sprint in ${until} ${until === 1 ? "day" : "days"}) LeetCode Daily Digest`,
|
||||
html,
|
||||
text,
|
||||
};
|
||||
|
||||
@@ -108,7 +108,7 @@ export interface TopicSection {
|
||||
export interface DigestData {
|
||||
/** "Tuesday, August 25" */
|
||||
day: string;
|
||||
/** "Day 9 of 79 · Week 2" — the length is CAMPAIGN_DAYS, derived from the plan. */
|
||||
/** "Day 9 of 30 · Week 2" of the sprint (SPRINT_DAYS, derived from the plan), or its countdown before day 1. */
|
||||
progress: string;
|
||||
streak: number;
|
||||
rest: boolean;
|
||||
|
||||
@@ -15,6 +15,12 @@ import {
|
||||
addDays,
|
||||
campaignDay,
|
||||
campaignWeek,
|
||||
CAMPAIGN_DAYS,
|
||||
CAMPAIGN_END,
|
||||
SPRINT_DAYS,
|
||||
SPRINT_START,
|
||||
sprintDay,
|
||||
sprintWeek,
|
||||
etDate,
|
||||
etHour,
|
||||
isRestDay,
|
||||
@@ -28,8 +34,8 @@ import {
|
||||
workingDay,
|
||||
} from "./srs.ts";
|
||||
|
||||
/** Every ET date of the campaign, plus the deferred-Hard tail. */
|
||||
const CALENDAR = Array.from({ length: 70 }, (_, i) => addDays("2026-08-17", i));
|
||||
/** Every ET date of the campaign (sprint included), plus two weeks of ladder tail. */
|
||||
const CALENDAR = Array.from({ length: CAMPAIGN_DAYS + 14 }, (_, i) => addDays("2026-08-17", i));
|
||||
|
||||
describe("DST-proof cron guard", () => {
|
||||
test("digest fires only at ET hour 8 — EDT (UTC-4)", () => {
|
||||
@@ -67,6 +73,15 @@ describe("date math (noon-UTC anchored)", () => {
|
||||
expect(campaignWeek("2026-08-30")).toBe(2);
|
||||
expect(campaignWeek("2026-08-31")).toBe(3);
|
||||
});
|
||||
test("sprint math: day 1 is SPRINT_START, the last is the plan's end, weeks run Mon–Sun", () => {
|
||||
expect(sprintDay(SPRINT_START)).toBe(1);
|
||||
expect(sprintDay(addDays(SPRINT_START, -1))).toBe(0); // countdown, never "Day 0 of N"
|
||||
expect(sprintDay(CAMPAIGN_END)).toBe(SPRINT_DAYS);
|
||||
expect(sprintWeek("2026-11-06")).toBe(1); // Friday start
|
||||
expect(sprintWeek("2026-11-08")).toBe(1); // its Sunday closes week 1
|
||||
expect(sprintWeek("2026-11-09")).toBe(2);
|
||||
expect(sprintWeek(CAMPAIGN_END)).toBe(5);
|
||||
});
|
||||
test("iso week is stable across a week", () => {
|
||||
expect(isoWeek("2026-08-24")).toBe(isoWeek("2026-08-29"));
|
||||
expect(isoWeek("2026-08-30")).toBe(isoWeek("2026-08-24")); // Sun ends the ISO week
|
||||
|
||||
+30
-2
@@ -166,6 +166,30 @@ export const SCHEDULE: Record<string, ScheduledDay> = scheduleJson;
|
||||
export const CAMPAIGN_END = Object.keys(SCHEDULE).sort().at(-1)!;
|
||||
export const CAMPAIGN_DAYS = daysBetween(CAMPAIGN_START, CAMPAIGN_END) + 1;
|
||||
|
||||
/**
|
||||
* Day 1 of the interview sprint: the plan restarted here after two weeks off,
|
||||
* with every open Review issue rebooked between this Friday and CAMPAIGN_END.
|
||||
* Only the start is declared; the length is derived like CAMPAIGN_DAYS, so
|
||||
* booking a later day lengthens both. The digest counts days and weeks from
|
||||
* here; the heatmaps and /api/stats keep the whole campaign.
|
||||
*/
|
||||
export const SPRINT_START = "2026-11-06";
|
||||
export const SPRINT_DAYS = daysBetween(SPRINT_START, CAMPAIGN_END) + 1;
|
||||
|
||||
/** 1-based sprint day; zero or negative before SPRINT_START. */
|
||||
export function sprintDay(date: string): number {
|
||||
return daysBetween(SPRINT_START, date) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1-based sprint week, Mon–Sun like the campaign's, so the Friday start makes
|
||||
* week 1 three days long and every later week a whole one.
|
||||
*/
|
||||
export function sprintWeek(date: string): number {
|
||||
const monday = addDays(SPRINT_START, -((weekdayOf(SPRINT_START) + 6) % 7));
|
||||
return Math.floor(daysBetween(monday, date) / 7) + 1;
|
||||
}
|
||||
|
||||
/** Date the schedule teaches a topic on, if it teaches it at all. */
|
||||
export function topicDate(topic: number): string | undefined {
|
||||
for (const [date, day] of Object.entries(SCHEDULE)) {
|
||||
@@ -413,8 +437,12 @@ export async function dueToday(db: D1Database, date: string): Promise<{ date: st
|
||||
};
|
||||
}
|
||||
|
||||
/** Reviews surfaced per day; the digest levels everything past this forward. */
|
||||
export const REVIEW_CAP = 3;
|
||||
/**
|
||||
* Reviews surfaced per day; the digest levels everything past this forward.
|
||||
* 5, not 3, since the two-week break: at 3 a day the ~110-review backlog would
|
||||
* outlast the sprint by a fortnight.
|
||||
*/
|
||||
export const REVIEW_CAP = 5;
|
||||
|
||||
/**
|
||||
* Load-level an overloaded review queue: everything due beyond today's
|
||||
|
||||
+219
-41
@@ -9,12 +9,20 @@
|
||||
* slug and kept forever. Only the campaign problems linked from README.md
|
||||
* are ever fetched, and paid-only problems are skipped because GraphQL
|
||||
* returns null content for them.
|
||||
* Everything goes through /graphql: Cloudflare challenges the REST endpoints
|
||||
* (`/api/problems/all/`, `/api/submissions/`) with a 403 for any non-browser
|
||||
* client, signed in or not.
|
||||
*
|
||||
* A `solutions` table tracks the lifecycle of each work/ file by LC number:
|
||||
* `created_at` (backfilled from the file's birthtime on every picker run),
|
||||
* `last_tested` and `last_submitted` (stamped on exit 0 of leetcode-cli).
|
||||
* The test picker sorts by last_tested, the submit picker by created_at, and
|
||||
* submit hides anything with a last_submitted.
|
||||
* The test picker sorts by last_tested, the submit picker by created_at.
|
||||
* Local stamps die with the db and birthtimes with a clone, so at most once
|
||||
* per 24h (`meta.history_fetched_at`) the signed-in profile's submission
|
||||
* history is folded in: a problem's first submission caps `created_at`, its
|
||||
* latest raises `last_submitted` and `last_tested` (a submission is a judge
|
||||
* run too; LeetCode keeps no record of `leetcode test` runs). Folding only
|
||||
* ever moves a timestamp outward, so a re-run is a no-op.
|
||||
*
|
||||
* Library: side-effect-free on import — nothing is opened or fetched until
|
||||
* problemDb() is called. Index upserts never touch the description column, so
|
||||
@@ -40,8 +48,8 @@ export interface SolutionActivity {
|
||||
lastSubmitted: string | null;
|
||||
}
|
||||
|
||||
const DIFFICULTY = ["", "Easy", "Medium", "Hard"] as const;
|
||||
const INDEX_TTL_MS = 24 * 60 * 60 * 1000;
|
||||
const DIFFICULTY = { EASY: "Easy", MEDIUM: "Medium", HARD: "Hard" } as const;
|
||||
const TTL_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
export const DB_PATH = join(
|
||||
process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"),
|
||||
@@ -62,50 +70,185 @@ export function readmeSlugs(markdown: string): string[] {
|
||||
|
||||
// ── remote fetches ───────────────────────────────────────────────
|
||||
|
||||
async function fetchIndex(): Promise<Problem[]> {
|
||||
const res = await fetch("https://leetcode.com/api/problems/all/", {
|
||||
headers: { "user-agent": "Mozilla/5.0" },
|
||||
});
|
||||
if (!res.ok) throw new Error(`GET /api/problems/all -> ${res.status} ${res.statusText}`);
|
||||
const data = (await res.json()) as {
|
||||
stat_status_pairs: Array<{
|
||||
stat: {
|
||||
frontend_question_id: number;
|
||||
question__title: string;
|
||||
question__title_slug: string;
|
||||
};
|
||||
difficulty: { level: 1 | 2 | 3 };
|
||||
paid_only: boolean;
|
||||
}>;
|
||||
};
|
||||
return data.stat_status_pairs
|
||||
.map((p) => ({
|
||||
id: p.stat.frontend_question_id,
|
||||
title: p.stat.question__title,
|
||||
slug: p.stat.question__title_slug,
|
||||
difficulty: DIFFICULTY[p.difficulty.level] as Problem["difficulty"],
|
||||
paidOnly: p.paid_only,
|
||||
}))
|
||||
.sort((a, b) => a.id - b.id);
|
||||
/** leetcode-cli's login: the two cookies every signed-in request carries. */
|
||||
export interface Credentials {
|
||||
session: string;
|
||||
csrfToken: string;
|
||||
}
|
||||
|
||||
/** HTML problem statement, or null when LeetCode withholds it (paid-only). */
|
||||
export async function fetchDescription(slug: string): Promise<string | null> {
|
||||
/**
|
||||
* POST one query to leetcode.com/graphql. Throws on an HTTP error or a
|
||||
* response with no `data`; partial `errors` beside data are the caller's.
|
||||
*/
|
||||
async function graphql<T>(
|
||||
query: string,
|
||||
variables: Record<string, unknown>,
|
||||
{ referer = "https://leetcode.com/", auth }: { referer?: string; auth?: Credentials } = {},
|
||||
): Promise<T> {
|
||||
const res = await fetch("https://leetcode.com/graphql", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"user-agent": "Mozilla/5.0",
|
||||
referer: `https://leetcode.com/problems/${slug}/`,
|
||||
referer,
|
||||
...(auth && {
|
||||
cookie: `LEETCODE_SESSION=${auth.session}; csrftoken=${auth.csrfToken}`,
|
||||
"x-csrftoken": auth.csrfToken,
|
||||
}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: "query questionContent($titleSlug: String!) { question(titleSlug: $titleSlug) { content } }",
|
||||
variables: { titleSlug: slug },
|
||||
}),
|
||||
body: JSON.stringify({ query, variables }),
|
||||
});
|
||||
if (!res.ok) throw new Error(`POST /graphql (${slug}) -> ${res.status} ${res.statusText}`);
|
||||
const data = (await res.json()) as { data?: { question?: { content?: string | null } | null } };
|
||||
return data.data?.question?.content ?? null;
|
||||
if (!res.ok) throw new Error(`POST /graphql -> ${res.status} ${res.statusText}`);
|
||||
const body = (await res.json()) as { data?: T | null; errors?: { message: string }[] };
|
||||
if (!body.data) {
|
||||
throw new Error(`POST /graphql: ${body.errors?.map((e) => e.message).join("; ") || "no data"}`);
|
||||
}
|
||||
return body.data;
|
||||
}
|
||||
|
||||
/** Server-side cap on `limit` for the problem list. */
|
||||
const INDEX_PAGE = 100;
|
||||
|
||||
async function fetchIndex(): Promise<Problem[]> {
|
||||
type Page = {
|
||||
problemsetQuestionListV2: {
|
||||
totalLength: number;
|
||||
questions: Array<{
|
||||
questionFrontendId: string;
|
||||
title: string;
|
||||
titleSlug: string;
|
||||
difficulty: keyof typeof DIFFICULTY;
|
||||
paidOnly: boolean;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
const page = async (skip: number) =>
|
||||
(
|
||||
await graphql<Page>(
|
||||
`query problemIndex($skip: Int!, $limit: Int!) {
|
||||
problemsetQuestionListV2(skip: $skip, limit: $limit) {
|
||||
totalLength
|
||||
questions { questionFrontendId title titleSlug difficulty paidOnly }
|
||||
}
|
||||
}`,
|
||||
{ skip, limit: INDEX_PAGE },
|
||||
{ referer: "https://leetcode.com/problemset/" },
|
||||
)
|
||||
).problemsetQuestionListV2;
|
||||
|
||||
// The first page names the total; the rest go six at a time.
|
||||
const first = await page(0);
|
||||
const questions = [...first.questions];
|
||||
const skips = Array.from({ length: Math.ceil(first.totalLength / INDEX_PAGE) - 1 }, (_, i) => (i + 1) * INDEX_PAGE);
|
||||
await Promise.all(
|
||||
Array.from({ length: 6 }, async () => {
|
||||
for (let skip = skips.shift(); skip !== undefined; skip = skips.shift()) {
|
||||
questions.push(...(await page(skip)).questions);
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (questions.length < first.totalLength) {
|
||||
throw new Error(`problem index came back short: ${questions.length} of ${first.totalLength}`);
|
||||
}
|
||||
|
||||
return questions
|
||||
.map((q) => ({
|
||||
id: Number(q.questionFrontendId),
|
||||
title: q.title,
|
||||
slug: q.titleSlug,
|
||||
difficulty: DIFFICULTY[q.difficulty],
|
||||
paidOnly: q.paidOnly,
|
||||
}))
|
||||
.filter((p) => Number.isInteger(p.id))
|
||||
.sort((a, b) => a.id - b.id);
|
||||
}
|
||||
|
||||
/** HTML problem statement, or null when LeetCode withholds it (paid-only). */
|
||||
export async function fetchDescription(slug: string): Promise<string | null> {
|
||||
const data = await graphql<{ question?: { content?: string | null } | null }>(
|
||||
"query questionContent($titleSlug: String!) { question(titleSlug: $titleSlug) { content } }",
|
||||
{ titleSlug: slug },
|
||||
{ referer: `https://leetcode.com/problems/${slug}/` },
|
||||
);
|
||||
return data.question?.content ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The login leetcode-cli holds, read from where it keeps it: its own
|
||||
* LEETCODE_SESSION + LEETCODE_CSRF_TOKEN override, else the OS keychain entry
|
||||
* `leetcode login` writes through keytar (service `leetcode-cli`, account
|
||||
* `auth`). Null when signed out, or when the login lives in leetcode-cli's
|
||||
* encrypted-file fallback, whose key is the CLI's own.
|
||||
*/
|
||||
export function leetcodeCredentials(): Credentials | null {
|
||||
const { LEETCODE_SESSION: session, LEETCODE_CSRF_TOKEN: csrfToken } = process.env;
|
||||
if (session && csrfToken) return { session, csrfToken };
|
||||
|
||||
const lookup =
|
||||
process.platform === "darwin"
|
||||
? ["security", "find-generic-password", "-s", "leetcode-cli", "-a", "auth", "-w"]
|
||||
: ["secret-tool", "lookup", "service", "leetcode-cli", "account", "auth"];
|
||||
let stored: string;
|
||||
try {
|
||||
const out = Bun.spawnSync(lookup, { stderr: "ignore" });
|
||||
if (!out.success) return null;
|
||||
stored = out.stdout.toString();
|
||||
} catch {
|
||||
return null; // no keychain CLI on this machine
|
||||
}
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(stored);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
!parsed ||
|
||||
typeof parsed !== "object" ||
|
||||
!("session" in parsed) ||
|
||||
typeof parsed.session !== "string" ||
|
||||
!("csrfToken" in parsed) ||
|
||||
typeof parsed.csrfToken !== "string"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return { session: parsed.session, csrfToken: parsed.csrfToken };
|
||||
}
|
||||
|
||||
/** One submission of the signed-in profile, any verdict. */
|
||||
export interface Submission {
|
||||
slug: string;
|
||||
/** ISO timestamp. */
|
||||
at: string;
|
||||
}
|
||||
|
||||
/** Server-side cap on `limit` for the submission list. */
|
||||
const SUBMISSION_PAGE = 20;
|
||||
|
||||
/** The signed-in profile's whole submission history, newest first. */
|
||||
export async function fetchSubmissions(auth: Credentials): Promise<Submission[]> {
|
||||
type Page = {
|
||||
submissionList: { hasNext: boolean; submissions: Array<{ titleSlug: string; timestamp: string }> } | null;
|
||||
};
|
||||
const all: Submission[] = [];
|
||||
for (let offset = 0; ; offset += SUBMISSION_PAGE) {
|
||||
const { submissionList } = await graphql<Page>(
|
||||
`query submissionList($offset: Int!, $limit: Int!) {
|
||||
submissionList(offset: $offset, limit: $limit) {
|
||||
hasNext
|
||||
submissions { titleSlug timestamp }
|
||||
}
|
||||
}`,
|
||||
{ offset, limit: SUBMISSION_PAGE },
|
||||
{ referer: "https://leetcode.com/submissions/", auth },
|
||||
);
|
||||
// An expired session is answered with a null list, not an HTTP error.
|
||||
if (!submissionList) throw new Error("submission history unavailable — session expired? run `leetcode login`");
|
||||
for (const s of submissionList.submissions) {
|
||||
all.push({ slug: s.titleSlug, at: new Date(Number(s.timestamp) * 1000).toISOString() });
|
||||
}
|
||||
if (!submissionList.hasNext) return all;
|
||||
}
|
||||
}
|
||||
|
||||
// ── the cache ────────────────────────────────────────────────────
|
||||
@@ -149,14 +292,28 @@ export function problemDb(path = DB_PATH) {
|
||||
const descState = db.query<{ paid_only: number; described: number }, [string]>(
|
||||
"SELECT paid_only, description IS NOT NULL AS described FROM problems WHERE slug = ?1",
|
||||
);
|
||||
// MIN/MAX on TEXT compare ISO timestamps chronologically; COALESCE because
|
||||
// SQLite's scalar MAX returns NULL when any argument is NULL.
|
||||
const fold = db.query<never, [number, string, string]>(
|
||||
`UPDATE solutions SET
|
||||
created_at = MIN(created_at, ?2),
|
||||
last_tested = MAX(COALESCE(last_tested, ''), ?3),
|
||||
last_submitted = MAX(COALESCE(last_submitted, ''), ?3)
|
||||
WHERE id = ?1`,
|
||||
);
|
||||
|
||||
/** True when `key` has never been stamped or was stamped over 24h ago. */
|
||||
const stale = (key: string) => {
|
||||
const row = getMeta.get(key);
|
||||
return !row || Date.now() - Number(row.value) > TTL_MS;
|
||||
};
|
||||
|
||||
return {
|
||||
path,
|
||||
|
||||
/** True when the index has never been fetched or is older than 24h. */
|
||||
indexStale(): boolean {
|
||||
const row = getMeta.get("index_fetched_at");
|
||||
return !row || Date.now() - Number(row.value) > INDEX_TTL_MS;
|
||||
return stale("index_fetched_at");
|
||||
},
|
||||
|
||||
/** Pull the full index from leetcode.com and upsert it. Returns the count. */
|
||||
@@ -228,6 +385,27 @@ export function problemDb(path = DB_PATH) {
|
||||
).run(id, new Date().toISOString());
|
||||
},
|
||||
|
||||
/** True when submission history has never been folded in or over 24h ago. */
|
||||
historyStale(): boolean {
|
||||
return stale("history_fetched_at");
|
||||
},
|
||||
|
||||
/**
|
||||
* Fold the profile's submissions (LC number → first and latest submission,
|
||||
* ISO) into existing solution rows and stamp the refresh. Rows are never
|
||||
* created here — the table holds work/ files only — and a timestamp only
|
||||
* ever moves outward, so folding twice changes nothing. Returns the number
|
||||
* of solutions that have submissions.
|
||||
*/
|
||||
foldHistory(history: Map<number, { first: string; last: string }>): number {
|
||||
let matched = 0;
|
||||
db.transaction(() => {
|
||||
for (const [id, { first, last }] of history) matched += fold.run(id, first, last).changes;
|
||||
setMeta.run("history_fetched_at", String(Date.now()));
|
||||
})();
|
||||
return matched;
|
||||
},
|
||||
|
||||
close(): void {
|
||||
db.close();
|
||||
},
|
||||
|
||||
+72
-7
@@ -9,18 +9,22 @@
|
||||
*
|
||||
* Every run backfills the db's solutions table with created_at (from the
|
||||
* file's birthtime, mtime when the filesystem has none) so ordering works for
|
||||
* files that predate the db. Ordering is per command — test lists the most
|
||||
* recently tested first (never-tested after, by name); submit lists the most
|
||||
* recently created first. Nothing is ever hidden. Exit 0 of leetcode-cli
|
||||
* stamps last_tested / last_submitted.
|
||||
* files that predate the db, and at most once a day folds in the signed-in
|
||||
* LeetCode profile's submission history (see db.ts), which survives a lost db
|
||||
* or a fresh clone. Files git reports as modified, added or
|
||||
* untracked come first, newest edit first — the problem being worked on is
|
||||
* the focused option, so Enter runs it without a search. The rest are ordered
|
||||
* per command — test lists the most recently tested first (never-tested
|
||||
* after, by name); submit lists the most recently created first. Nothing is
|
||||
* ever hidden. Exit 0 of leetcode-cli stamps last_tested / last_submitted.
|
||||
*
|
||||
* Library: side-effect-free on import — nothing scans the filesystem until
|
||||
* runOnSolution() is called.
|
||||
*/
|
||||
import { autocomplete, cancel, isCancel, log } from "@clack/prompts";
|
||||
import { autocomplete, cancel, isCancel, log, spinner } from "@clack/prompts";
|
||||
import { statSync } from "node:fs";
|
||||
import { basename, join, relative } from "node:path";
|
||||
import { problemDb } from "./db";
|
||||
import { fetchSubmissions, leetcodeCredentials, problemDb } from "./db";
|
||||
import { ROOT, WORK as WORK_DIR, parseWorkPath } from "./work";
|
||||
|
||||
/** Leading LC number of a work/ file, or undefined for unnumbered names. */
|
||||
@@ -29,6 +33,29 @@ function lcNumber(rel: string): number | undefined {
|
||||
return m ? Number(m[1]) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* work/-relative paths of solutions git sees as modified, added or untracked,
|
||||
* mapped to their mtime. Empty outside a git checkout.
|
||||
*/
|
||||
function dirtySolutions(scanned: Set<string>): Map<string, number> {
|
||||
const git = Bun.spawnSync(
|
||||
["git", "status", "--porcelain", "-z", "--untracked-files=all", "--", WORK_DIR],
|
||||
{ cwd: ROOT },
|
||||
);
|
||||
const dirty = new Map<string, number>();
|
||||
if (!git.success) return dirty;
|
||||
const entries = git.stdout.toString().split("\0");
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i]!;
|
||||
// renames/copies carry their source path as the next entry; skip it
|
||||
if (/[RC]/.test(entry.slice(0, 2))) i++;
|
||||
const rel = relative(WORK_DIR, join(ROOT, entry.slice(3)));
|
||||
// deletions and non-solution files are not in the scan
|
||||
if (scanned.has(rel)) dirty.set(rel, statSync(join(WORK_DIR, rel)).mtimeMs);
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
/** Fuzzy-pick a solution under work/ and run `leetcode <command>` on it. */
|
||||
export async function runOnSolution(
|
||||
command: "test" | "submit",
|
||||
@@ -52,6 +79,36 @@ export async function runOnSolution(
|
||||
new Date(stat.birthtimeMs || stat.mtimeMs).toISOString(),
|
||||
);
|
||||
}
|
||||
|
||||
// the profile's history: first and latest submission per work/ problem
|
||||
if (db.historyStale()) {
|
||||
const auth = leetcodeCredentials();
|
||||
if (!auth) {
|
||||
log.warn("Not signed in to leetcode-cli — run `leetcode login` to restore history from your profile.");
|
||||
} else {
|
||||
const s = spinner();
|
||||
s.start("Syncing submission history from your LeetCode profile");
|
||||
try {
|
||||
const bySlug = new Map<string, number>();
|
||||
for (const f of scanned) {
|
||||
const parsed = parseWorkPath(f);
|
||||
if (parsed) bySlug.set(parsed.slug, parsed.lc);
|
||||
}
|
||||
const history = new Map<number, { first: string; last: string }>();
|
||||
for (const { slug, at } of await fetchSubmissions(auth)) {
|
||||
const id = bySlug.get(slug);
|
||||
if (id === undefined) continue;
|
||||
const seen = history.get(id);
|
||||
if (!seen) history.set(id, { first: at, last: at });
|
||||
else if (at < seen.first) seen.first = at;
|
||||
else if (at > seen.last) seen.last = at;
|
||||
}
|
||||
s.stop(`Synced submission history for ${db.foldHistory(history)} solutions`);
|
||||
} catch (err) {
|
||||
s.error(`Submission history sync failed (${err}) — using cached timestamps`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const activity = db.solutionActivity();
|
||||
|
||||
// test → most recently tested first; submit → most recently created first
|
||||
@@ -61,7 +118,15 @@ export async function runOnSolution(
|
||||
return (command === "test" ? row?.lastTested : row?.createdAt) ?? undefined;
|
||||
};
|
||||
|
||||
const dirty = dirtySolutions(new Set(scanned));
|
||||
const files = scanned.sort((a, b) => {
|
||||
const ma = dirty.get(a);
|
||||
const mb = dirty.get(b);
|
||||
if (ma !== mb) {
|
||||
if (ma === undefined) return 1;
|
||||
if (mb === undefined) return -1;
|
||||
return mb - ma;
|
||||
}
|
||||
const ka = sortKey(a);
|
||||
const kb = sortKey(b);
|
||||
if (ka !== kb) {
|
||||
@@ -91,7 +156,7 @@ export async function runOnSolution(
|
||||
value: f,
|
||||
label: basename(f),
|
||||
hint: parsed
|
||||
? `${stage} · ${parsed.difficulty} · ${parsed.category}`
|
||||
? `${dirty.has(f) ? "uncommitted · " : ""}${stage} · ${parsed.difficulty} · ${parsed.category}`
|
||||
: f,
|
||||
};
|
||||
}),
|
||||
|
||||
+94
-2
@@ -27,6 +27,15 @@
|
||||
* (never on a sync with nothing new); offline, or with no `gh` auth, the
|
||||
* page is written without it and the next sync retries. Delete the key to
|
||||
* refetch it.
|
||||
* - Every page carries the companies that asked it in the last six months as
|
||||
* frontmatter `companies:` (`{ name, period, frequency }`, the most recent
|
||||
* window each company falls in and its frequency there, most recent first;
|
||||
* `apps/docs/companies.ts`), read from the local DuckDB
|
||||
* company-frequency database (`COMPANY_DB`, built by its own `mise run
|
||||
* lc:build`) with the `duckdb` CLI. Unlike the keys above it is rewritten
|
||||
* every run, since recency ages and a rebuilt database should show; a
|
||||
* problem no company asked lately carries no key. Where the database or the
|
||||
* CLI is missing (CI), the step is skipped and pages keep the key as is.
|
||||
* - Every tag a page carries that no campaign topic teaches (not a key of
|
||||
* `apps/docs/topic-landings.ts`) belongs in the Appendix glossary,
|
||||
* `apps/docs/appendix.ts`, which is hand-written: sync only checks it,
|
||||
@@ -63,10 +72,13 @@
|
||||
* `apps/docs/redirects.ts`, which `blume.config.ts` imports — Blume takes exact
|
||||
* root-relative paths and prepends the deployment base itself.
|
||||
*/
|
||||
import { existsSync } from "node:fs";
|
||||
import { mkdir, readdir, rename, rmdir, unlink } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { basename, dirname, join } from "node:path";
|
||||
|
||||
import { APPENDIX } from "../docs/appendix.ts";
|
||||
import { RECENCY, type Company } from "../docs/companies.ts";
|
||||
import { TOPIC_LANDINGS } from "../docs/topic-landings.ts";
|
||||
import { github } from "./github.ts";
|
||||
|
||||
@@ -506,6 +518,62 @@ async function issueUrl(sol: Solution): Promise<string | undefined> {
|
||||
return url;
|
||||
}
|
||||
|
||||
// ── company frequency ────────────────────────────────────────────
|
||||
|
||||
// The windows and the key's shape are `apps/docs/companies.ts`, shared with
|
||||
// the schema and the readers; `More Than Six Months` and `All` are not recent.
|
||||
const COMPANY_DB = join(
|
||||
process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"),
|
||||
"duckdb",
|
||||
"data",
|
||||
"leetcode-company-frequency.db",
|
||||
);
|
||||
|
||||
/**
|
||||
* LC number → the companies that asked it in the last six months, each at the
|
||||
* most recent window it appears in (the windows nest), ordered most recent
|
||||
* first, then by that window's frequency. Undefined when this machine has no
|
||||
* database or no `duckdb` CLI — the step is skipped — or the query fails
|
||||
* (warned). One query for every solved problem.
|
||||
*/
|
||||
function recentCompanies(nums: number[]): Map<number, Company[]> | undefined {
|
||||
if (!existsSync(COMPANY_DB) || !Bun.which("duckdb")) return undefined;
|
||||
const order = `list_position([${RECENCY.map((p) => `'${p}'`).join(", ")}], period)`;
|
||||
const sql = `SELECT question_id, company, period, frequency FROM company_frequency
|
||||
WHERE question_id IN (${nums.join(", ")}) AND ${order} IS NOT NULL
|
||||
QUALIFY row_number() OVER (PARTITION BY question_id, company ORDER BY ${order}) = 1
|
||||
ORDER BY question_id, ${order}, frequency DESC, company`;
|
||||
const proc = Bun.spawnSync(["duckdb", "-readonly", "-json", COMPANY_DB, "-c", sql]);
|
||||
if (!proc.success) {
|
||||
console.warn(`no companies: duckdb -> ${proc.stderr.toString().trim()}`);
|
||||
return undefined;
|
||||
}
|
||||
const out = proc.stdout.toString().trim();
|
||||
const rows: unknown = out ? JSON.parse(out) : [];
|
||||
const companies = new Map<number, Company[]>(nums.map((n) => [n, []]));
|
||||
for (const row of Array.isArray(rows) ? rows : []) {
|
||||
if (!row || typeof row !== "object") continue;
|
||||
if (!("question_id" in row) || typeof row.question_id !== "number") continue;
|
||||
if (!("company" in row) || typeof row.company !== "string") continue;
|
||||
if (!("period" in row) || !("frequency" in row) || typeof row.frequency !== "number") continue;
|
||||
const period = RECENCY.find((p) => p === row.period);
|
||||
if (period)
|
||||
companies.get(row.question_id)?.push({ name: row.company, period, frequency: row.frequency });
|
||||
}
|
||||
return companies;
|
||||
}
|
||||
|
||||
/** The frontmatter `companies:` block, one flow mapping per company (JSON strings are valid YAML). */
|
||||
function companiesYaml(list: Company[]): string {
|
||||
return [
|
||||
"companies:",
|
||||
...list.map(
|
||||
(c) =>
|
||||
` - { name: ${JSON.stringify(c.name)}, period: ${JSON.stringify(c.period)}, frequency: ${c.frequency} }`,
|
||||
),
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
// ── inline-code formatting heuristics ────────────────────────────
|
||||
|
||||
/** Identifiers worth backticking in prose, harvested from the code. */
|
||||
@@ -624,6 +692,7 @@ function renderPage(
|
||||
sol: Solution,
|
||||
tags: TopicTag[] | undefined,
|
||||
issue: string | undefined,
|
||||
companies: Company[] | undefined,
|
||||
): string {
|
||||
const h = sol.header;
|
||||
const ids = identifiers(sol);
|
||||
@@ -639,6 +708,7 @@ function renderPage(
|
||||
`icon: ${PAGE_ICON}`,
|
||||
...(tags ? [topicsYaml(tags)] : []),
|
||||
...(issue ? [`issue: ${JSON.stringify(issue)}`] : []),
|
||||
...(companies?.length ? [companiesYaml(companies)] : []),
|
||||
"sidebar:",
|
||||
` label: '${h.title.replaceAll("'", "''")}'`,
|
||||
` badge: '${h.difficulty}'`,
|
||||
@@ -704,6 +774,7 @@ for await (const rel of new Bun.Glob("**/*.{js,py}").scan(WORK)) {
|
||||
}
|
||||
|
||||
const topics = await readmeTopics();
|
||||
const companies = recentCompanies([...solutions.keys()]);
|
||||
// An unplanned problem whose work/ category is also a README topic name (a
|
||||
// `Hash Table` warm-up) files under that topic, in its phase, not a second
|
||||
// root-level `hash-table/`.
|
||||
@@ -753,6 +824,23 @@ async function ensureIssue(page: string, sol: Solution): Promise<string> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the frontmatter's `companies:` with this run's list, inserted before
|
||||
* `sidebar:` (dropped when no company asked lately). Without the database the
|
||||
* page is returned untouched, so an environment lacking it never strips the key.
|
||||
*/
|
||||
function ensureCompanies(page: string, sol: Solution): string {
|
||||
if (!companies) return page;
|
||||
const fm = page.match(/^---\n[\s\S]*?\n---/);
|
||||
if (!fm) return page;
|
||||
const list = companies.get(sol.num) ?? [];
|
||||
const without = fm[0].replace(/^companies:.*\n(?: {2}- .*\n)*/m, "");
|
||||
const next = list.length
|
||||
? without.replace(/^sidebar:$/m, `${companiesYaml(list)}\nsidebar:`)
|
||||
: without;
|
||||
return next + page.slice(fm[0].length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset just past the script-owned `## Solution` section: the next top-level
|
||||
* heading that is not inside a code fence, or end of file. Sections a human
|
||||
@@ -815,8 +903,11 @@ for (const sol of ordered) {
|
||||
const tail = page.slice(solutionEnd(page, at));
|
||||
const body =
|
||||
page.slice(0, at) + solutionSection(sol) + (tail ? `\n${tail}` : "");
|
||||
const next = await ensureIssue(
|
||||
await ensureTopics(ensureIcon(ensureLabel(body, sol.header)), sol),
|
||||
const next = ensureCompanies(
|
||||
await ensureIssue(
|
||||
await ensureTopics(ensureIcon(ensureLabel(body, sol.header)), sol),
|
||||
sol,
|
||||
),
|
||||
sol,
|
||||
);
|
||||
const actions: string[] = [];
|
||||
@@ -836,6 +927,7 @@ for (const sol of ordered) {
|
||||
sol,
|
||||
await fetchTopicTags(sol),
|
||||
await issueUrl(sol),
|
||||
companies?.get(sol.num),
|
||||
);
|
||||
await Bun.write(join(DOCS, name), page);
|
||||
report.push([workRef, name, "created"]);
|
||||
|
||||
+31
-15
@@ -2,6 +2,7 @@ import { defineConfig } from "blume";
|
||||
import type { StandardSchema } from "blume/core/standard-schema.ts";
|
||||
import { filesystem } from "blume/sources";
|
||||
import { APPENDIX } from "./appendix";
|
||||
import { RECENCY, type Company } from "./companies";
|
||||
import redirects from "./redirects";
|
||||
|
||||
// Blume's defaults (`inter`, `ibm-plex-mono`) are fetched from fonts.gstatic.com
|
||||
@@ -33,13 +34,14 @@ const plexMono = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom frontmatter, rendered as the badge row by
|
||||
* Custom frontmatter, rendered between the description and the body by
|
||||
* components/ProblemBadges.astro. Problem pages: `topics:` is LeetCode's topic
|
||||
* tags and `issue:` the problem's GitHub issue URL (both written by
|
||||
* apps/cli/sync.ts), `video:` the NeetCode walkthrough's
|
||||
* YouTube id (apps/cli/videos.ts). Blume rejects undeclared keys and takes
|
||||
* any Standard Schema; these are hand-rolled so the docs workspace needs no
|
||||
* zod. All optional — every other page carries none.
|
||||
* tags, `issue:` the problem's GitHub issue URL and `companies:` the companies
|
||||
* that asked it in the last six months (`companies.ts`; the table under the
|
||||
* badge row) — all three written by apps/cli/sync.ts — and `video:` the
|
||||
* NeetCode walkthrough's YouTube id (apps/cli/videos.ts). Blume rejects
|
||||
* undeclared keys and takes any Standard Schema; these are hand-rolled so the
|
||||
* docs workspace needs no zod. All optional — every other page carries none.
|
||||
*/
|
||||
const optional = <T>(
|
||||
message: string,
|
||||
@@ -74,6 +76,17 @@ const issueSchema = optional(
|
||||
typeof value === "string" &&
|
||||
/^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/issues\/\d+$/.test(value),
|
||||
);
|
||||
const companiesSchema = optional(
|
||||
`expected a list of { name, period, frequency } with period one of ${RECENCY.join(" | ")}`,
|
||||
(value): value is Company[] =>
|
||||
Array.isArray(value) &&
|
||||
value.every(
|
||||
(c) =>
|
||||
typeof c?.name === "string" &&
|
||||
RECENCY.includes(c?.period) &&
|
||||
typeof c?.frequency === "number",
|
||||
),
|
||||
);
|
||||
|
||||
export default defineConfig({
|
||||
title: "prdlk",
|
||||
@@ -102,6 +115,7 @@ export default defineConfig({
|
||||
topics: topicsSchema,
|
||||
video: videoSchema,
|
||||
issue: issueSchema,
|
||||
companies: companiesSchema,
|
||||
},
|
||||
},
|
||||
seo: {
|
||||
@@ -127,10 +141,11 @@ export default defineConfig({
|
||||
llmsTxt: true,
|
||||
mcp: { enabled: false },
|
||||
},
|
||||
// The only content source. `/changelog` and `/appendix` are custom pages
|
||||
// (pages/*.astro): `/changelog` reads the GitHub releases itself — one
|
||||
// timeline, no per-release pages — and `/appendix` is one glossary page
|
||||
// built from appendix.ts, so neither enters the sidebar tree or prev/next.
|
||||
// The only content source. `/changelog`, `/appendix` and `/progress` are
|
||||
// custom pages (pages/*.astro): `/changelog` reads the GitHub releases
|
||||
// itself — one timeline, no per-release pages — `/appendix` is one glossary
|
||||
// page built from appendix.ts, and `/progress` is the live stats cards, so
|
||||
// none enters the sidebar tree or prev/next.
|
||||
content: {
|
||||
sources: [filesystem({ root: "content" })],
|
||||
},
|
||||
@@ -142,11 +157,12 @@ export default defineConfig({
|
||||
sidebar: {
|
||||
display: "flat", // "flat" | "group" | "page"
|
||||
},
|
||||
// The glossary is no content folder any more, so it is pinned above the
|
||||
// sidebar tree, on every page and breakpoint.
|
||||
featured: [
|
||||
{ label: "Changelog", href: "/changelog", icon: "clock" },
|
||||
{ label: "Appendix", href: "/appendix", icon: "library" },
|
||||
// The custom pages (pages/*.astro) as header tabs. None is a content
|
||||
// folder, so the tabs scope nothing out of the sidebar tree.
|
||||
tabs: [
|
||||
{ label: "Changelog", path: "/changelog" },
|
||||
{ label: "Progress", path: "/progress" },
|
||||
{ label: "Appendix", path: "/appendix" },
|
||||
],
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* The `companies:` frontmatter vocabulary, shared by its writer
|
||||
* (`apps/cli/sync.ts`, from the local DuckDB company-frequency database), its
|
||||
* schema (`blume.config.ts`) and its two readers: the company table under a
|
||||
* problem page's badge row (`components/ProblemBadges.astro`) and the
|
||||
* Companies section of the Appendix (`pages/appendix.astro`), where each
|
||||
* company's card — anchored at `companyHref()` — lists the problems it asked.
|
||||
* Plain data, no Astro imports: sync imports this file under Bun.
|
||||
*/
|
||||
|
||||
/** The database's recency windows, most recent first; they nest, so a company is filed under the first it appears in. */
|
||||
export const RECENCY = ["Thirty Days", "Three Months", "Six Months"] as const;
|
||||
export type Recency = (typeof RECENCY)[number];
|
||||
|
||||
/** Read under an "Asked within" heading (the company table) or as a row chip (the Appendix card). */
|
||||
export const RECENCY_LABEL: Record<Recency, string> = {
|
||||
"Thirty Days": "30 days",
|
||||
"Three Months": "3 months",
|
||||
"Six Months": "6 months",
|
||||
};
|
||||
|
||||
export interface Company {
|
||||
name: string;
|
||||
/** The most recent window the company asked the problem in. */
|
||||
period: Recency;
|
||||
/** 5–100, relative within the company and window: 100 is its most-asked problem there. */
|
||||
frequency: number;
|
||||
}
|
||||
|
||||
/** The company's card on the Appendix; `company-` keeps it clear of the tag cards' `#<slug>` ids. */
|
||||
export function companyHref(name: string): string {
|
||||
return `/appendix#company-${name
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-|-$/g, "")}`;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
/**
|
||||
* The MDX `wrapper`: the badge row between a page's description and its body.
|
||||
* The MDX `wrapper`: the badge row (and, below it, the company table) between
|
||||
* a page's description and its body.
|
||||
* Blume's page template renders the title and description itself and then
|
||||
* the MDX content, with no slot in between, so wrapping the content is the
|
||||
* only way to land right after the description. MDX hands a wrapper no
|
||||
@@ -26,12 +27,19 @@
|
||||
* - `issue:` (problem pages, written by sync) is the problem's GitHub issue
|
||||
* URL, a neutral "Issue #N" badge with the GitHub mark, right after
|
||||
* "Explanation" at the end of the row.
|
||||
* - `companies:` (problem pages, written by sync; `companies.ts`) is the
|
||||
* companies that asked the problem in the last six months, a table laid
|
||||
* out like Blume's TypeTable under the badge row: each company a pill
|
||||
* linking to its card in the Appendix's Companies section, the most recent
|
||||
* window it asked in, and its frequency there (relative within that
|
||||
* company and window, so a bar of 100 is its most-asked problem).
|
||||
*/
|
||||
import { getEntry } from "astro:content";
|
||||
import data from "blume:data";
|
||||
import Badge from "blume/components/content/Badge.astro";
|
||||
import { stripBase, withBase } from "blume/components/islands/base-path.ts";
|
||||
import { APPENDIX } from "../appendix.ts";
|
||||
import { RECENCY_LABEL, companyHref, type Company } from "../companies.ts";
|
||||
import { tagColors } from "../lib/tag-colors.ts";
|
||||
import { TOPIC_LANDINGS, tagTarget, type TopicTag } from "../topic-landings.ts";
|
||||
|
||||
@@ -46,6 +54,7 @@ const entry = at ? await getEntry(at.collection, at.entryId) : undefined;
|
||||
const tags: TopicTag[] = entry?.data.topics ?? [];
|
||||
const video: string | undefined = entry?.data.video;
|
||||
const issue: string | undefined = entry?.data.issue;
|
||||
const companies: Company[] = entry?.data.companies ?? [];
|
||||
|
||||
const served = new Set<string>(data.routes.map((r: { path: string }) => r.path));
|
||||
|
||||
@@ -96,6 +105,8 @@ const badges = tags.map((tag) => {
|
||||
});
|
||||
|
||||
const linkClass = "no-underline transition-opacity hover:opacity-80";
|
||||
// TypeTable's column track, so the header and every row align.
|
||||
const columns = "grid grid-cols-[2fr_1fr_auto] items-center gap-4";
|
||||
---
|
||||
|
||||
{
|
||||
@@ -131,4 +142,34 @@ const linkClass = "no-underline transition-opacity hover:opacity-80";
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
companies.length > 0 && (
|
||||
<div class="not-prose mb-6 rounded-blume border border-border bg-muted/30 p-1.5" data-blume-narration="skip">
|
||||
<div class={`${columns} px-3 py-2 font-medium text-muted-foreground text-xs`}>
|
||||
<span>Company</span>
|
||||
<span>Asked within</span>
|
||||
<span title="Relative within the company and window: 100 is its most-asked problem there">Frequency</span>
|
||||
</div>
|
||||
<div class="mt-1 flex flex-col">
|
||||
{companies.map((c) => (
|
||||
<div class={`${columns} border-border border-t px-3 py-2 text-xs`}>
|
||||
<a href={withBase(companyHref(c.name))} class={linkClass}>
|
||||
<Badge icon="building-2" shape="pill" stroke>
|
||||
{c.name}
|
||||
</Badge>
|
||||
</a>
|
||||
<span class="whitespace-nowrap text-foreground">{RECENCY_LABEL[c.period]}</span>
|
||||
<span class="flex items-center gap-2">
|
||||
<span class="h-1.5 w-16 overflow-hidden rounded-full bg-muted" aria-hidden="true">
|
||||
<span class="block h-full rounded-full bg-accent" style={`width: ${c.frequency}%`} />
|
||||
</span>
|
||||
<code class="w-8 text-right font-mono text-foreground tabular-nums">{Math.round(c.frequency)}</code>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<slot />
|
||||
@@ -5,12 +5,10 @@
|
||||
* hover. A row links to the problem's docs page when one exists: the
|
||||
* sync-generated short links (`redirects.ts`) are exactly the pages built.
|
||||
*/
|
||||
import { StatsPending } from "../components/progress/shared";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
|
||||
import { useStats, utcDate } from "../lib/stats";
|
||||
import redirects from "../redirects";
|
||||
|
||||
export const client = "load";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
|
||||
import { useStats, utcDate } from "../../lib/stats";
|
||||
import redirects from "../../redirects";
|
||||
import { StatsPending } from "./shared";
|
||||
|
||||
const PAGES = new Map(redirects.map((r) => [r.from, r.to]));
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
*/
|
||||
import { useState } from "react";
|
||||
import { Pie, PieChart } from "recharts";
|
||||
import { StatsPending } from "../components/progress/shared";
|
||||
import { Card, CardContent } from "../components/ui/card";
|
||||
import { type ChartConfig, ChartContainer } from "../components/ui/chart";
|
||||
import { DIFFICULTIES, type Difficulty, useStats } from "../lib/stats";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
export const client = "load";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { type ChartConfig, ChartContainer } from "../ui/chart";
|
||||
import { DIFFICULTIES, type Difficulty, useStats } from "../../lib/stats";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { StatsPending } from "./shared";
|
||||
|
||||
const chartConfig = {
|
||||
easy: { label: "Easy", color: "var(--chart-1)" },
|
||||
+5
-7
@@ -9,13 +9,11 @@
|
||||
*/
|
||||
import { useState } from "react";
|
||||
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
|
||||
import { StatsPending } from "../components/progress/shared";
|
||||
import { Card, CardAction, CardContent, CardFooter, CardHeader } from "../components/ui/card";
|
||||
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "../components/ui/chart";
|
||||
import { ToggleGroup, ToggleGroupItem } from "../components/ui/toggle-group";
|
||||
import { DIFFICULTIES, type Day, type Difficulty, useStats, utcDate } from "../lib/stats";
|
||||
|
||||
export const client = "load";
|
||||
import { Card, CardAction, CardContent, CardFooter, CardHeader } from "../ui/card";
|
||||
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "../ui/chart";
|
||||
import { ToggleGroup, ToggleGroupItem } from "../ui/toggle-group";
|
||||
import { DIFFICULTIES, type Day, type Difficulty, useStats, utcDate } from "../../lib/stats";
|
||||
import { StatsPending } from "./shared";
|
||||
|
||||
const chartConfig = {
|
||||
easy: { label: "Easy", color: "var(--chart-1)" },
|
||||
+3
-5
@@ -8,11 +8,9 @@
|
||||
* 4) and rows start on Monday like it, so the three heatmaps read the same.
|
||||
* Drawn as SVG in a viewBox, so it scales to its column instead of scrolling.
|
||||
*/
|
||||
import { StatsPending, TipRow, useHoverTip } from "../components/progress/shared";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card";
|
||||
import { type Day, useStats, utcDate } from "../lib/stats";
|
||||
|
||||
export const client = "load";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
|
||||
import { type Day, useStats, utcDate } from "../../lib/stats";
|
||||
import { StatsPending, TipRow, useHoverTip } from "./shared";
|
||||
|
||||
const MONTHS = 6;
|
||||
const CELL = 10;
|
||||
@@ -9,12 +9,10 @@
|
||||
*/
|
||||
import { packEnclose, packSiblings } from "d3-hierarchy";
|
||||
import { useId } from "react";
|
||||
import { StatsPending, TipRow, useHoverTip } from "../components/progress/shared";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card";
|
||||
import { type Stats, useStats } from "../lib/stats";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
export const client = "load";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
|
||||
import { type Stats, useStats } from "../../lib/stats";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { StatsPending, TipRow, useHoverTip } from "./shared";
|
||||
|
||||
const PAD = 2; // gap between bubbles, in layout units
|
||||
const UNIT = 12; // radius of a one-problem bubble
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* What the four progress islands share besides the data: the not-ready body
|
||||
* What the progress cards share besides the data: the not-ready body
|
||||
* (a skeleton while /api/stats loads, one muted line if it fails) and the
|
||||
* hover tooltip for the two charts drawn as plain SVG (the heatmap calendar
|
||||
* and the topic bubbles — Recharts has neither layout). The tooltip wears the
|
||||
|
||||
+7
@@ -7,6 +7,13 @@ topics:
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "nIVW4P8b1VA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/62"
|
||||
companies:
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 65.6 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 43.5 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 40.6 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 32.7 }
|
||||
sidebar:
|
||||
label: 'Find Minimum in Rotated Sorted Array'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -6,6 +6,15 @@ topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/64"
|
||||
companies:
|
||||
- { name: "Infosys", period: "Thirty Days", frequency: 68.6 }
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 67.6 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 54.8 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 36.8 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 34.6 }
|
||||
- { name: "Uber", period: "Six Months", frequency: 67.2 }
|
||||
sidebar:
|
||||
label: 'Find Peak Element'
|
||||
badge: 'Medium'
|
||||
|
||||
+12
@@ -7,6 +7,18 @@ topics:
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "U8XENwh8Oy8"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/60"
|
||||
companies:
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 36.7 }
|
||||
- { name: "Oracle", period: "Three Months", frequency: 100 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 68.3 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 33.4 }
|
||||
- { name: "TikTok", period: "Six Months", frequency: 64.9 }
|
||||
- { name: "LinkedIn", period: "Six Months", frequency: 63.3 }
|
||||
- { name: "Yandex", period: "Six Months", frequency: 59.6 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 53.1 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 48.1 }
|
||||
sidebar:
|
||||
label: 'Search in Rotated Sorted Array'
|
||||
badge: 'Medium'
|
||||
|
||||
+11
@@ -6,6 +6,17 @@ topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/65"
|
||||
companies:
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 57.2 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 36.7 }
|
||||
- { name: "TikTok", period: "Three Months", frequency: 70.9 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 47.7 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 43.3 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 36.8 }
|
||||
- { name: "Instacart", period: "Six Months", frequency: 100 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 70.2 }
|
||||
- { name: "Infosys", period: "Six Months", frequency: 56.2 }
|
||||
sidebar:
|
||||
label: 'Find First and Last Position of Element in Sorted Array'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -7,6 +7,15 @@ topics:
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "K-RYzDZkzCI"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/63"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 50.2 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Grammarly", period: "Three Months", frequency: 84.8 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 49.8 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 47.7 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Infosys", period: "Six Months", frequency: 56.2 }
|
||||
sidebar:
|
||||
label: 'Search Insert Position'
|
||||
badge: 'Easy'
|
||||
|
||||
+13
@@ -7,6 +7,19 @@ topics:
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
- { name: "Divide and Conquer", slug: "divide-and-conquer" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/66"
|
||||
companies:
|
||||
- { name: "Rippling", period: "Thirty Days", frequency: 93 }
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 69.6 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 63.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 62 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 57.2 }
|
||||
- { name: "eBay", period: "Three Months", frequency: 82.2 }
|
||||
- { name: "Goldman Sachs", period: "Three Months", frequency: 57 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 47.7 }
|
||||
- { name: "Zoho", period: "Six Months", frequency: 79.6 }
|
||||
- { name: "DE Shaw", period: "Six Months", frequency: 74.2 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 48.1 }
|
||||
sidebar:
|
||||
label: 'Median of Two Sorted Arrays'
|
||||
badge: 'Hard'
|
||||
|
||||
@@ -7,6 +7,12 @@ topics:
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "s4DPM8ct1pI"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/58"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 53.2 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 49.8 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 39.1 }
|
||||
sidebar:
|
||||
label: 'Binary Search'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -8,6 +8,15 @@ topics:
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
video: "Ber2pi2C0j0"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/61"
|
||||
companies:
|
||||
- { name: "Goldman Sachs", period: "Thirty Days", frequency: 62.9 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 57.2 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 34.6 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "SAP", period: "Six Months", frequency: 100 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 43.5 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
sidebar:
|
||||
label: 'Search a 2D Matrix'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -7,6 +7,18 @@ topics:
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "U2SozAs9RzA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/59"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 69.6 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Nvidia", period: "Three Months", frequency: 100 }
|
||||
- { name: "Infosys", period: "Three Months", frequency: 87.4 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 62.3 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Coupang", period: "Six Months", frequency: 100 }
|
||||
- { name: "Swiggy", period: "Six Months", frequency: 100 }
|
||||
- { name: "Accenture", period: "Six Months", frequency: 75 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 56.9 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 44.8 }
|
||||
sidebar:
|
||||
label: 'Koko Eating Bananas'
|
||||
badge: 'Medium'
|
||||
|
||||
+12
@@ -7,6 +7,18 @@ topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Union-Find", slug: "union-find" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/34"
|
||||
companies:
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 74.3 }
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 65.8 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 57.2 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 53.2 }
|
||||
- { name: "Apple", period: "Three Months", frequency: 69 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Paytm", period: "Six Months", frequency: 100 }
|
||||
- { name: "Nvidia", period: "Six Months", frequency: 91.1 }
|
||||
- { name: "TikTok", period: "Six Months", frequency: 55.2 }
|
||||
- { name: "Salesforce", period: "Six Months", frequency: 50.2 }
|
||||
sidebar:
|
||||
label: 'Longest Consecutive Sequence'
|
||||
badge: 'Medium'
|
||||
|
||||
+4
@@ -5,6 +5,10 @@ icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 34.2 }
|
||||
- { name: "Amazon", period: "Six Months", frequency: 14.3 }
|
||||
- { name: "Google", period: "Six Months", frequency: 11.8 }
|
||||
sidebar:
|
||||
label: 'Check if the Sentence Is Pangram'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -7,6 +7,35 @@ topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
video: "KLlXCFG5TnA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/29"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 79.6 }
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 76.7 }
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Infosys", period: "Thirty Days", frequency: 68.6 }
|
||||
- { name: "Altimetrik", period: "Three Months", frequency: 100 }
|
||||
- { name: "Capgemini", period: "Three Months", frequency: 100 }
|
||||
- { name: "Microstrategy", period: "Three Months", frequency: 100 }
|
||||
- { name: "MongoDB", period: "Three Months", frequency: 100 }
|
||||
- { name: "Cognizant", period: "Three Months", frequency: 88.7 }
|
||||
- { name: "Apple", period: "Three Months", frequency: 80.4 }
|
||||
- { name: "Barclays", period: "Six Months", frequency: 100 }
|
||||
- { name: "Cisco", period: "Six Months", frequency: 100 }
|
||||
- { name: "Deloitte", period: "Six Months", frequency: 100 }
|
||||
- { name: "Expedia", period: "Six Months", frequency: 100 }
|
||||
- { name: "NetApp", period: "Six Months", frequency: 100 }
|
||||
- { name: "Samsung", period: "Six Months", frequency: 100 }
|
||||
- { name: "Sony", period: "Six Months", frequency: 100 }
|
||||
- { name: "Oracle", period: "Six Months", frequency: 91.2 }
|
||||
- { name: "Qualcomm", period: "Six Months", frequency: 80.7 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 77.2 }
|
||||
- { name: "eBay", period: "Six Months", frequency: 75.2 }
|
||||
- { name: "Walmart Labs", period: "Six Months", frequency: 70.5 }
|
||||
- { name: "Flipkart", period: "Six Months", frequency: 57.1 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 44.8 }
|
||||
sidebar:
|
||||
label: 'Two Sum'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -6,6 +6,13 @@ topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/33"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 36.7 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 47.7 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 33.4 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 24.5 }
|
||||
sidebar:
|
||||
label: 'Isomorphic Strings'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -8,6 +8,12 @@ topics:
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "3OamzN90kPg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/27"
|
||||
companies:
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 50.2 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 48.3 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 36.8 }
|
||||
sidebar:
|
||||
label: 'Contains Duplicate'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -8,6 +8,16 @@ topics:
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "9UtInBqnCgA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/28"
|
||||
companies:
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 58.2 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 53.2 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "IBM", period: "Three Months", frequency: 100 }
|
||||
- { name: "Infosys", period: "Three Months", frequency: 66 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 64.4 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 58.9 }
|
||||
- { name: "Deloitte", period: "Six Months", frequency: 81 }
|
||||
sidebar:
|
||||
label: 'Valid Anagram'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -6,6 +6,10 @@ topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/35"
|
||||
companies:
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 24.1 }
|
||||
- { name: "Amazon", period: "Six Months", frequency: 20.9 }
|
||||
sidebar:
|
||||
label: 'Word Pattern'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -13,6 +13,23 @@ topics:
|
||||
- { name: "Quickselect", slug: "quickselect" }
|
||||
video: "YPTqKIgVk-k"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/31"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 72.9 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 42.3 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Snap", period: "Six Months", frequency: 100 }
|
||||
- { name: "SoFi", period: "Six Months", frequency: 100 }
|
||||
- { name: "PayPal", period: "Six Months", frequency: 88.4 }
|
||||
- { name: "Walmart Labs", period: "Six Months", frequency: 81.4 }
|
||||
- { name: "Nvidia", period: "Six Months", frequency: 78.7 }
|
||||
- { name: "eBay", period: "Six Months", frequency: 75.2 }
|
||||
- { name: "Visa", period: "Six Months", frequency: 69.7 }
|
||||
- { name: "Snowflake", period: "Six Months", frequency: 58.7 }
|
||||
- { name: "TikTok", period: "Six Months", frequency: 55.2 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 50.6 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 48.1 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 43.5 }
|
||||
sidebar:
|
||||
label: 'Top K Frequent Elements'
|
||||
badge: 'Medium'
|
||||
|
||||
+10
@@ -8,6 +8,16 @@ topics:
|
||||
- { name: "Queue", slug: "queue" }
|
||||
- { name: "Counting", slug: "counting" }
|
||||
video: "rBENYgWy3xU"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "Yandex", period: "Three Months", frequency: 76.1 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 73.7 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 46.8 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 43.3 }
|
||||
- { name: "Google", period: "Three Months", frequency: 23.9 }
|
||||
- { name: "EPAM Systems", period: "Six Months", frequency: 76.2 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 59 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 32 }
|
||||
sidebar:
|
||||
label: 'First Unique Character in a String'
|
||||
badge: 'Easy'
|
||||
|
||||
+6
@@ -10,6 +10,12 @@ topics:
|
||||
- { name: "Bucket Sort", slug: "bucket-sort" }
|
||||
- { name: "Counting", slug: "counting" }
|
||||
video: "OXdXc9HTrIg"
|
||||
companies:
|
||||
- { name: "Meta", period: "Three Months", frequency: 49.8 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 48.3 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 46.8 }
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "Amazon", period: "Six Months", frequency: 20.9 }
|
||||
sidebar:
|
||||
label: 'Sort Characters By Frequency'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -9,6 +9,31 @@ topics:
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "vzdNOK2oB2E"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/30"
|
||||
companies:
|
||||
- { name: "Apple", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 78.4 }
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 76.7 }
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 58.2 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 50.2 }
|
||||
- { name: "CrowdStrike", period: "Three Months", frequency: 100 }
|
||||
- { name: "Deloitte", period: "Three Months", frequency: 100 }
|
||||
- { name: "HashedIn", period: "Three Months", frequency: 100 }
|
||||
- { name: "Smartsheet", period: "Three Months", frequency: 100 }
|
||||
- { name: "Walmart Labs", period: "Three Months", frequency: 100 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 33.4 }
|
||||
- { name: "Expedia", period: "Six Months", frequency: 100 }
|
||||
- { name: "Wipro", period: "Six Months", frequency: 100 }
|
||||
- { name: "Adobe", period: "Six Months", frequency: 93.7 }
|
||||
- { name: "Zoho", period: "Six Months", frequency: 79.6 }
|
||||
- { name: "Nvidia", period: "Six Months", frequency: 78.7 }
|
||||
- { name: "Oracle", period: "Six Months", frequency: 78.7 }
|
||||
- { name: "EPAM Systems", period: "Six Months", frequency: 76.2 }
|
||||
- { name: "Palo Alto Networks", period: "Six Months", frequency: 75.9 }
|
||||
- { name: "Visa", period: "Six Months", frequency: 69.7 }
|
||||
- { name: "Infosys", period: "Six Months", frequency: 67.9 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 53.1 }
|
||||
sidebar:
|
||||
label: 'Group Anagrams'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { defineMeta } from "blume";
|
||||
|
||||
export default defineMeta({ title: "1. Linear Structures", order: 1 });
|
||||
export default defineMeta({ title: "Linear Structures", order: 1 });
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: '496. Next Greater Element I'
|
||||
description: The next greater element of some element x in an array is the first greater element that is to the right of x in the same array
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/74"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 46.6 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 59.7 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 43.3 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 33.4 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 43.5 }
|
||||
sidebar:
|
||||
label: 'Next Greater Element I'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Could you find an O(nums1.length + nums2.length) solution?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums1 = [4,1,2], nums2 = [1,3,4,2]`
|
||||
- Output: `[-1,3,-1]`
|
||||
- Explanation:
|
||||
- The next greater element for each value of `nums1` is as follows:
|
||||
- `4` is underlined in `nums2 = [1,3,4,2]`. There is no next greater element, so the answer is `-1`.
|
||||
- `1` is underlined in `nums2 = [1,3,4,2]`. The next greater element is `3`.
|
||||
- `2` is underlined in `nums2 = [1,3,4,2]`. There is no next greater element, so the answer is `-1`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums1 = [2,4], nums2 = [1,2,3,4]`
|
||||
- Output: `[3,-1]`
|
||||
- Explanation:
|
||||
- The next greater element for each value of `nums1` is as follows:
|
||||
- `2` is underlined in `nums2 = [1,2,3,4]`. The next greater element is `3`.
|
||||
- `4` is underlined in `nums2 = [1,2,3,4]`. There is no next greater element, so the answer is `-1`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums1.length <= nums2.length <= 1000`
|
||||
- `0 <= nums1[i], nums2[i] <= 10^4`
|
||||
- All integers in `nums1` and `nums2` are unique.
|
||||
- All the integers of `nums1` also appear in nums2.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def nextGreaterElement(self, nums1: list[int], nums2: list[int]) -> list[int]:
|
||||
mono_stack = []
|
||||
next_greater = {}
|
||||
|
||||
for cur in nums2:
|
||||
while mono_stack and cur > mono_stack[-1]:
|
||||
val = mono_stack.pop()
|
||||
next_greater[val] = cur
|
||||
mono_stack.append(cur)
|
||||
|
||||
return [next_greater.get(x, -1) for x in nums1]
|
||||
```
|
||||
@@ -8,6 +8,16 @@ topics:
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
video: "cTBiBSnjO3c"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/72"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Chime", period: "Three Months", frequency: 100 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 59.7 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 42.8 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Anduril", period: "Six Months", frequency: 92.8 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 44.8 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 43.5 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 42.5 }
|
||||
sidebar:
|
||||
label: 'Daily Temperatures'
|
||||
badge: 'Medium'
|
||||
|
||||
+8
@@ -8,6 +8,14 @@ topics:
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
- { name: "Range Minimum/Maximum Query", slug: "range-minimum-maximum-query" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/76"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Accenture", period: "Three Months", frequency: 88.6 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 73.7 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 36.8 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
sidebar:
|
||||
label: 'Largest Rectangle in Histogram'
|
||||
badge: 'Hard'
|
||||
|
||||
@@ -9,6 +9,9 @@ topics:
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
video: "Pr6T-3yB9RM"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/73"
|
||||
companies:
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "Amazon", period: "Six Months", frequency: 26 }
|
||||
sidebar:
|
||||
label: 'Car Fleet'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: '901. Online Stock Span'
|
||||
description: Design an algorithm that collects daily price quotes for some stock and returns the span of that stock's price for the current day
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Design", slug: "design" }
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
- { name: "Data Stream", slug: "data-stream" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/75"
|
||||
companies:
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "Amazon", period: "Six Months", frequency: 26 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
sidebar:
|
||||
label: 'Online Stock Span'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: ``
|
||||
- Output: ``
|
||||
- Explanation: Input ["StockSpanner", "next", "next", "next", "next", "next", "next", "next"] `[[]`, `[100]`, `[80]`, `[60]`, `[70]`, `[60]`, `[75]`, [85]] Output [null, `1`, `1`, `1`, `2`, `1`, `4`, 6] Explanation StockSpanner stockSpanner = new StockSpanner(); stockSpanner.next(100); // return `1` stockSpanner.next(80); // return `1` stockSpanner.next(60); // return `1` stockSpanner.next(70); // return `2` stockSpanner.next(60); // return `1` stockSpanner.next(75); // return `4`, because the last `4` prices (including today's `price` of `75`) were less than or equal to today's `price`. stockSpanner.next(85); // return `6`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= price <= 10^5`
|
||||
- At most 10^4 calls will be made to next.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class StockSpanner:
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
|
||||
def next(self, price: int) -> int:
|
||||
ans = 1
|
||||
while self.stack and self.stack[-1][0] <= price:
|
||||
ans += self.stack.pop()[1]
|
||||
|
||||
self.stack.append([price, ans])
|
||||
return ans
|
||||
|
||||
|
||||
# Your StockSpanner object will be instantiated and called as such:
|
||||
# obj = StockSpanner()
|
||||
# param_1 = obj.next(price)
|
||||
```
|
||||
@@ -3,3 +3,34 @@ title: Monotonic Stack
|
||||
description: 'A stack keeps its items in sorted order, which finds the next larger or smaller item in one pass.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
|
||||
<YouTube id="Dq_ObZwTY_Q" title="Monotonic Stack" />
|
||||
|
||||
A monotonic stack keeps its values in sorted order as you push elements. The order can be increasing or decreasing, depending on the problem.
|
||||
|
||||
A decreasing monotonic stack is the most common version. Before pushing a new value `x`, we pop from the top while the top is smaller than or equal to `x`. After those pops, pushing `x` keeps the stack decreasing from bottom to top.
|
||||
|
||||
This push rule is the whole idea. A monotonic deque uses the same rule, but a deque also lets you remove old elements from the front, which is useful for sliding window problems.
|
||||
|
||||
## Example Implementation
|
||||
|
||||
This implementation maintains a decreasing monotonic stack.
|
||||
|
||||
```python
|
||||
def mono_stack(insert_entries):
|
||||
stack = []
|
||||
for entry in insert_entries:
|
||||
# Pop smaller (or equal) values so stack stays decreasing.
|
||||
while stack and stack[-1] <= entry:
|
||||
stack.pop()
|
||||
# Do something with the popped item here
|
||||
stack.append(entry)
|
||||
```
|
||||
|
||||
:::tip
|
||||
In interview problems, the stack usually stores indices instead of values. You can read the value from the original array when needed, and the index gives you distance information such as **"how many steps away is the next greater element."**
|
||||
:::
|
||||
|
||||
For an array `nums`, if index `j` pops when you process index `i`, then `i` is the first index to the right that satisfies the stack condition for `j`. In a next-greater-element problem, that means `nums[i]` is the next greater value for `nums[j]`.
|
||||
|
||||
+8
@@ -7,6 +7,14 @@ topics:
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
video: "bNvIQI2wAjk"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/37"
|
||||
companies:
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 70.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 61.3 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 57.2 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 42.3 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 59.7 }
|
||||
- { name: "Asana", period: "Six Months", frequency: 100 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 43.5 }
|
||||
sidebar:
|
||||
label: 'Product of Array Except Self'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -8,6 +8,11 @@ topics:
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
video: "2pndAmo_sMA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/36"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 42.3 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 24.5 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 24.1 }
|
||||
sidebar:
|
||||
label: 'Range Sum Query - Immutable'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -7,6 +7,14 @@ topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/39"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Cisco", period: "Three Months", frequency: 100 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 49.8 }
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "Akamai", period: "Six Months", frequency: 100 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 40.6 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 32 }
|
||||
sidebar:
|
||||
label: 'Contiguous Array'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -8,6 +8,21 @@ topics:
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
video: "fFVZt-6sgyo"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/38"
|
||||
companies:
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 63.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 50.2 }
|
||||
- { name: "Visa", period: "Three Months", frequency: 80.9 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 74.8 }
|
||||
- { name: "Salesforce", period: "Three Months", frequency: 64.7 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 57.1 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 48.3 }
|
||||
- { name: "Accenture", period: "Six Months", frequency: 100 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 94.5 }
|
||||
- { name: "Infosys", period: "Six Months", frequency: 76.3 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 74.7 }
|
||||
- { name: "josh technology", period: "Six Months", frequency: 74 }
|
||||
- { name: "Walmart Labs", period: "Six Months", frequency: 70.5 }
|
||||
- { name: "Yandex", period: "Six Months", frequency: 69.2 }
|
||||
sidebar:
|
||||
label: 'Subarray Sum Equals K'
|
||||
badge: 'Medium'
|
||||
|
||||
+6
@@ -7,6 +7,12 @@ topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/40"
|
||||
companies:
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "TikTok", period: "Three Months", frequency: 70.9 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 21.2 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 24.1 }
|
||||
sidebar:
|
||||
label: 'Subarray Sums Divisible by K'
|
||||
badge: 'Medium'
|
||||
|
||||
+11
@@ -8,6 +8,17 @@ topics:
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/56"
|
||||
companies:
|
||||
- { name: "EPAM Systems", period: "Thirty Days", frequency: 89.8 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Tekion", period: "Three Months", frequency: 100 }
|
||||
- { name: "DE Shaw", period: "Three Months", frequency: 80.2 }
|
||||
- { name: "Google", period: "Three Months", frequency: 38.7 }
|
||||
- { name: "Infosys", period: "Six Months", frequency: 67.9 }
|
||||
- { name: "Yandex", period: "Six Months", frequency: 59.6 }
|
||||
- { name: "LinkedIn", period: "Six Months", frequency: 54.1 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 42.5 }
|
||||
sidebar:
|
||||
label: 'Max Consecutive Ones III'
|
||||
badge: 'Medium'
|
||||
|
||||
+20
@@ -7,6 +7,26 @@ topics:
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
video: "1pkOgXD63yU"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/50"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 83.1 }
|
||||
- { name: "Infosys", period: "Thirty Days", frequency: 80.2 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 69.6 }
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 65.6 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 65.3 }
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 58.2 }
|
||||
- { name: "Atlassian", period: "Three Months", frequency: 100 }
|
||||
- { name: "Media.net", period: "Three Months", frequency: 100 }
|
||||
- { name: "Nvidia", period: "Three Months", frequency: 87.7 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 70.4 }
|
||||
- { name: "Apple", period: "Three Months", frequency: 69 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 60.5 }
|
||||
- { name: "Mastercard", period: "Six Months", frequency: 79 }
|
||||
- { name: "Accenture", period: "Six Months", frequency: 75 }
|
||||
- { name: "Adobe", period: "Six Months", frequency: 74.3 }
|
||||
- { name: "Walmart Labs", period: "Six Months", frequency: 70.5 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 70.1 }
|
||||
- { name: "TikTok", period: "Six Months", frequency: 55.2 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 53.1 }
|
||||
sidebar:
|
||||
label: 'Best Time to Buy and Sell Stock'
|
||||
badge: 'Easy'
|
||||
|
||||
+6
@@ -8,6 +8,12 @@ topics:
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/54"
|
||||
companies:
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 46.8 }
|
||||
- { name: "Google", period: "Three Months", frequency: 45 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 28.9 }
|
||||
sidebar:
|
||||
label: 'Minimum Size Subarray Sum'
|
||||
badge: 'Medium'
|
||||
|
||||
+29
@@ -8,6 +8,35 @@ topics:
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
video: "wiGpQwVHdE0"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/51"
|
||||
companies:
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Visa", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 84.9 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 75.6 }
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 63.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 63.7 }
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 58.2 }
|
||||
- { name: "Cognizant", period: "Three Months", frequency: 100 }
|
||||
- { name: "Optum", period: "Three Months", frequency: 100 }
|
||||
- { name: "Spotify", period: "Three Months", frequency: 100 }
|
||||
- { name: "Deloitte", period: "Three Months", frequency: 89.1 }
|
||||
- { name: "Infosys", period: "Three Months", frequency: 87.4 }
|
||||
- { name: "Apple", period: "Three Months", frequency: 69 }
|
||||
- { name: "Goldman Sachs", period: "Three Months", frequency: 66.3 }
|
||||
- { name: "Media.net", period: "Six Months", frequency: 100 }
|
||||
- { name: "Wipro", period: "Six Months", frequency: 100 }
|
||||
- { name: "Wissen Technology", period: "Six Months", frequency: 100 }
|
||||
- { name: "Zoho", period: "Six Months", frequency: 100 }
|
||||
- { name: "Qualcomm", period: "Six Months", frequency: 92 }
|
||||
- { name: "Turing", period: "Six Months", frequency: 89 }
|
||||
- { name: "EPAM Systems", period: "Six Months", frequency: 86.7 }
|
||||
- { name: "Netflix", period: "Six Months", frequency: 83.1 }
|
||||
- { name: "Tesla", period: "Six Months", frequency: 80.9 }
|
||||
- { name: "Yandex", period: "Six Months", frequency: 69.2 }
|
||||
- { name: "Agoda", period: "Six Months", frequency: 65 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 60.1 }
|
||||
- { name: "LinkedIn", period: "Six Months", frequency: 54.1 }
|
||||
sidebar:
|
||||
label: 'Longest Substring Without Repeating Characters'
|
||||
badge: 'Medium'
|
||||
|
||||
+9
@@ -8,6 +8,15 @@ topics:
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
video: "gqXU1UyA8pk"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/52"
|
||||
companies:
|
||||
- { name: "Zoho", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 46.6 }
|
||||
- { name: "DE Shaw", period: "Three Months", frequency: 91.8 }
|
||||
- { name: "Infosys", period: "Three Months", frequency: 66 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 46.8 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 34.6 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 32.7 }
|
||||
sidebar:
|
||||
label: 'Longest Repeating Character Replacement'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -9,6 +9,14 @@ topics:
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
video: "UbyhOgBN834"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/53"
|
||||
companies:
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 63.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 47.7 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 46.8 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 33.4 }
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 48.1 }
|
||||
sidebar:
|
||||
label: 'Permutation in String'
|
||||
badge: 'Medium'
|
||||
|
||||
+7
@@ -6,6 +6,13 @@ topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/55"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 83.1 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 55.9 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 36.7 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 57.2 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 32.7 }
|
||||
sidebar:
|
||||
label: 'Maximum Average Subarray I'
|
||||
badge: 'Easy'
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: '76. Minimum Window Substring'
|
||||
description: Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return the empty string ""
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/57"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 55.9 }
|
||||
- { name: "Lyft", period: "Three Months", frequency: 93.3 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 48.3 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Google", period: "Three Months", frequency: 23.9 }
|
||||
- { name: "Oracle", period: "Six Months", frequency: 78.7 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 44.8 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
sidebar:
|
||||
label: 'Minimum Window Substring'
|
||||
badge: 'Hard'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Could you find an algorithm that runs in O(m + n) time?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "ADOBECODEBANC", t = "ABC"`
|
||||
- Output: `"BANC"`
|
||||
- Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string `t`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "a", t = "a"`
|
||||
- Output: `"a"`
|
||||
- Explanation: The entire string `s` is the minimum window.
|
||||
|
||||
### Example 3:
|
||||
- Input: `s = "a", t = "aa"`
|
||||
- Output: `""`
|
||||
- Explanation: Both 'a's from `t` must be included in the window. Since the largest window of `s` only has one 'a', return empty string.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `m == s.length`
|
||||
- `n == t.length`
|
||||
- `1 <= m, n <= 10^5`
|
||||
- `s` and `t` consist of uppercase and lowercase English letters.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def minWindow(self, s: str, t: str) -> str:
|
||||
```
|
||||
+6
@@ -8,6 +8,12 @@ topics:
|
||||
- { name: "Stack", slug: "stack" }
|
||||
video: "iu0082c4HDE"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/69"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "Yandex", period: "Six Months", frequency: 59.6 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 38.7 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
sidebar:
|
||||
label: 'Evaluate Reverse Polish Notation'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -7,6 +7,15 @@ topics:
|
||||
- { name: "Design", slug: "design" }
|
||||
video: "qkLl7nAwDPo"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/68"
|
||||
companies:
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 28.9 }
|
||||
- { name: "Google", period: "Three Months", frequency: 28.8 }
|
||||
- { name: "Odoo", period: "Six Months", frequency: 100 }
|
||||
- { name: "UiPath", period: "Six Months", frequency: 100 }
|
||||
- { name: "Infosys", period: "Six Months", frequency: 67.9 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 45.6 }
|
||||
sidebar:
|
||||
label: 'Min Stack'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -8,6 +8,24 @@ topics:
|
||||
- { name: "Bracket Sequences", slug: "bracket-sequences" }
|
||||
video: "WTzjTskDFMg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/67"
|
||||
companies:
|
||||
- { name: "BlackRock", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 61.3 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 42.3 }
|
||||
- { name: "Accolite", period: "Three Months", frequency: 100 }
|
||||
- { name: "Intuit", period: "Three Months", frequency: 73.2 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 68.3 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 65.3 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 57.2 }
|
||||
- { name: "Autodesk", period: "Six Months", frequency: 100 }
|
||||
- { name: "Criteo", period: "Six Months", frequency: 100 }
|
||||
- { name: "Odoo", period: "Six Months", frequency: 100 }
|
||||
- { name: "Tripadvisor", period: "Six Months", frequency: 100 }
|
||||
- { name: "Virtu Financial", period: "Six Months", frequency: 100 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 91.1 }
|
||||
- { name: "Epic Systems", period: "Six Months", frequency: 89.6 }
|
||||
- { name: "LinkedIn", period: "Six Months", frequency: 85.6 }
|
||||
sidebar:
|
||||
label: 'Valid Parentheses'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: '224. Basic Calculator'
|
||||
description: Given a string s representing a valid expression, implement a basic calculator to evaluate it, and return the result of the evaluation
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Recursion", slug: "recursion" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/71"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 28.9 }
|
||||
- { name: "Google", period: "Three Months", frequency: 23.9 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 24.5 }
|
||||
sidebar:
|
||||
label: 'Basic Calculator'
|
||||
badge: 'Hard'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "1 + 1"`
|
||||
- Output: `2`
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = " 2-1 + 2 "`
|
||||
- Output: `3`
|
||||
|
||||
### Example 3:
|
||||
- Input: `s = "(1+(4+5+2)-3)+(6+8)"`
|
||||
- Output: `23`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length <= 3 * 10^5`
|
||||
- `s` consists of digits, '+', '-', '(', ')', and ' '.
|
||||
- `s` represents a valid expression.
|
||||
- '+' is not used as a unary operation (i.e., "+1" and "+(2 + 3)" is invalid).
|
||||
- '-' could be used as a unary operation (i.e., "-1" and "-(2 + 3)" is valid).
|
||||
- There will be no two consecutive operators in the input.
|
||||
- Every number and running calculation will fit in a signed 32-bit integer.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def calculate(self, s: str) -> int:
|
||||
```
|
||||
@@ -6,6 +6,13 @@ topics:
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/70"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Google", period: "Three Months", frequency: 28.8 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 21.2 }
|
||||
- { name: "Snowflake", period: "Six Months", frequency: 58.7 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 50.6 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 24.1 }
|
||||
sidebar:
|
||||
label: 'Simplify Path'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -3,3 +3,60 @@ title: Stack
|
||||
description: 'Items are added and removed from one end, so the last item added is the first item removed.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
A stack works like a stack of plates in a cafeteria. You add new plates on top. When you need a plate, you take from the top. The last plate you added is the first one you remove.
|
||||
|
||||
This is **Last-In-First-Out (LIFO)**. The most recent item goes out first.
|
||||
|
||||

|
||||
|
||||
## Core Stack Operations
|
||||
|
||||
A stack has two fundamental operations:
|
||||
|
||||
- **Push**: add an item to the top of the stack
|
||||
- **Pop**: remove the item at the top of the stack
|
||||
|
||||
Some stacks have additional operations:
|
||||
|
||||
- **Peek**: get the item at the top of the stack without removing it
|
||||
- **IsEmpty**: check if the stack is empty
|
||||
|
||||

|
||||
|
||||
```python
|
||||
# Using Python list as a stack
|
||||
stack = []
|
||||
|
||||
# Push elements
|
||||
stack.append(10)
|
||||
stack.append(20)
|
||||
stack.append(30)
|
||||
# Stack: [10, 20, 30] (30 is on top)
|
||||
|
||||
# Pop element
|
||||
top = stack.pop() # Returns 30
|
||||
# Stack: [10, 20]
|
||||
|
||||
# Peek at top
|
||||
if stack:
|
||||
top_element = stack[-1] # Returns 20 without removing
|
||||
```
|
||||
|
||||
Python lists provide `append()` for pushing and `pop()` for popping. The `peek()` method is not built-in, but you can implement it yourself.
|
||||
|
||||
## Last in, First out
|
||||
|
||||
The defining characteristic of a stack is LIFO order. Whatever you put in last comes out first. This is different from a queue, which is First-In-First-Out (FIFO).
|
||||
|
||||
Add items: A, B, C
|
||||
|
||||
- Stack: [A, B, C] (C on top)
|
||||
|
||||
Remove items:
|
||||
|
||||
- First pop: C
|
||||
- Second pop: B
|
||||
- Third pop: A
|
||||
|
||||
The order reverses. Items come out in the opposite order they went in.
|
||||
+12
@@ -8,6 +8,18 @@ topics:
|
||||
- { name: "Greedy", slug: "greedy" }
|
||||
video: "UuiTKBwPgAo"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/44"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 83.1 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 75.8 }
|
||||
- { name: "Infosys", period: "Thirty Days", frequency: 68.6 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 67.6 }
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 65.6 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 62 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Goldman Sachs", period: "Three Months", frequency: 57 }
|
||||
- { name: "Walmart Labs", period: "Six Months", frequency: 70.5 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 60.1 }
|
||||
sidebar:
|
||||
label: 'Container With Most Water'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -7,6 +7,18 @@ topics:
|
||||
- { name: "String", slug: "string" }
|
||||
video: "jJXJ16kPFWg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/41"
|
||||
companies:
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 58.2 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 79.9 }
|
||||
- { name: "Infosys", period: "Three Months", frequency: 66 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Deloitte", period: "Six Months", frequency: 81 }
|
||||
- { name: "Cognizant", period: "Six Months", frequency: 76.3 }
|
||||
- { name: "Axon", period: "Six Months", frequency: 75.4 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 64.8 }
|
||||
sidebar:
|
||||
label: 'Valid Palindrome'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -8,6 +8,22 @@ topics:
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "jzZsG8n2R9A"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/43"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 69.6 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 66.8 }
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 65.6 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 63.8 }
|
||||
- { name: "Apple", period: "Three Months", frequency: 80.4 }
|
||||
- { name: "Infosys", period: "Three Months", frequency: 66 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 57.2 }
|
||||
- { name: "Cisco", period: "Six Months", frequency: 100 }
|
||||
- { name: "Wissen Technology", period: "Six Months", frequency: 100 }
|
||||
- { name: "eBay", period: "Six Months", frequency: 75.2 }
|
||||
- { name: "Accenture", period: "Six Months", frequency: 75 }
|
||||
- { name: "Visa", period: "Six Months", frequency: 69.7 }
|
||||
- { name: "Goldman Sachs", period: "Six Months", frequency: 53.1 }
|
||||
sidebar:
|
||||
label: '3Sum'
|
||||
badge: 'Medium'
|
||||
|
||||
+8
@@ -8,6 +8,14 @@ topics:
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "cQ1Oz4ckceM"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/42"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 65.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 46.6 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 57.2 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 54.8 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 33.4 }
|
||||
- { name: "Oracle", period: "Six Months", frequency: 78.7 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 40.6 }
|
||||
sidebar:
|
||||
label: 'Two Sum II - Input Array Is Sorted'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -7,6 +7,17 @@ topics:
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/47"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 46.6 }
|
||||
- { name: "Infosys", period: "Three Months", frequency: 66 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 57.2 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 54.8 }
|
||||
- { name: "Box", period: "Six Months", frequency: 100 }
|
||||
- { name: "TikTok", period: "Six Months", frequency: 55.2 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 48.1 }
|
||||
sidebar:
|
||||
label: 'Rotate Array'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -6,6 +6,14 @@ topics:
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "String", slug: "string" }
|
||||
video: "_d0T_2Lk2qA"
|
||||
companies:
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 58.2 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 50.2 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 48.3 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 46.8 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Qualcomm", period: "Six Months", frequency: 80.7 }
|
||||
sidebar:
|
||||
label: 'Reverse String'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -8,6 +8,12 @@ topics:
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
video: "99RVfqklbCE"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/45"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Three Months", frequency: 34.6 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
- { name: "Google", period: "Six Months", frequency: 25.4 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 24.5 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 24.1 }
|
||||
sidebar:
|
||||
label: 'Is Subsequence'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -9,6 +9,20 @@ topics:
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/211"
|
||||
companies:
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 90.7 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 80.7 }
|
||||
- { name: "Goldman Sachs", period: "Thirty Days", frequency: 71.7 }
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Infosys", period: "Thirty Days", frequency: 68.6 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 63.7 }
|
||||
- { name: "Snowflake", period: "Three Months", frequency: 90.3 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 58.9 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 57.2 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 47.7 }
|
||||
- { name: "Docusign", period: "Six Months", frequency: 100 }
|
||||
- { name: "Qualcomm", period: "Six Months", frequency: 80.7 }
|
||||
- { name: "Nvidia", period: "Six Months", frequency: 78.7 }
|
||||
sidebar:
|
||||
label: 'Trapping Rain Water'
|
||||
badge: 'Hard'
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: '80. Remove Duplicates from Sorted Array II'
|
||||
description: Given an integer array nums sorted in non-decreasing order, remove some duplicates in-place such that each unique element appears at most twice. The relative order of the elements should be kept the same
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/48"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 34.6 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 32.7 }
|
||||
sidebar:
|
||||
label: 'Remove Duplicates from Sorted Array II'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,1,1,2,2,3]`
|
||||
- Output: `5, nums = [1,1,2,2,3,_]`
|
||||
- Explanation: Your function should return `k = 5`, with the first five elements of `nums` being `1`, `1`, `2`, `2` and `3` respectively. It does not matter what you leave beyond the returned `k` (hence they are underscores).
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [0,0,1,1,1,1,2,3,3]`
|
||||
- Output: `7, nums = [0,0,1,1,2,3,3,_,_]`
|
||||
- Explanation: Your function should return `k = 7`, with the first seven elements of `nums` being `0`, `0`, `1`, `1`, `2`, `3` and `3` respectively. It does not matter what you leave beyond the returned `k` (hence they are underscores).
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 3 * 10^4`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
- `nums` is sorted in non-decreasing order.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def removeDuplicates(self, nums: list[int]) -> int:
|
||||
slow = 0
|
||||
for fast in range(len(nums)):
|
||||
if slow < 2 or nums[fast] != nums[slow - 2]:
|
||||
nums[slow] = nums[fast]
|
||||
slow += 1
|
||||
return slow
|
||||
```
|
||||
+9
@@ -8,6 +8,15 @@ topics:
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "FPCZsG_AkUg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/46"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 42.3 }
|
||||
- { name: "Instacart", period: "Three Months", frequency: 100 }
|
||||
- { name: "Uber", period: "Three Months", frequency: 80 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 49.8 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 46 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 60 }
|
||||
- { name: "Yandex", period: "Six Months", frequency: 59.6 }
|
||||
sidebar:
|
||||
label: 'Squares of a Sorted Array'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -3,3 +3,160 @@ title: Two Pointers
|
||||
description: 'Two indices move through the data from different positions and meet at the answer.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
Most interview problems in this section fall into one of four families:
|
||||
|
||||
- **Same direction pointers**: one pointer reads, one pointer writes, or one pointer stays behind another while scanning left to right.
|
||||
- **Opposite direction pointers**: one pointer starts on the left, one on the right, and each move eliminates impossible answers.
|
||||
- **Sliding window**: the two pointers define a contiguous interval, and you maintain some property of that interval as the window expands and shrinks.
|
||||
- **Fast and slow pointers**: both pointers move through a linked structure, but at different speeds or with a fixed gap.
|
||||
|
||||

|
||||
|
||||
## Same Direction
|
||||
|
||||
Same-direction pointer problems usually scan from left to right, but the two pointers do different jobs.
|
||||
|
||||
Usually:
|
||||
|
||||
- one pointer reads the next value from the original input
|
||||
- one pointer marks where the next kept value should go
|
||||
|
||||
Sometimes both pointers move forward together, but one pointer stays behind the other to preserve a gap or a relationship.
|
||||
|
||||
### When this family applies
|
||||
|
||||
|
||||
Reach for same-direction pointers when the problem asks you to:
|
||||
|
||||
- rewrite an array in-place
|
||||
- remove or compress elements without extra memory
|
||||
- keep only the values that satisfy some condition
|
||||
- maintain a fixed gap between two forward-moving pointers
|
||||
|
||||
### Core Invariant
|
||||
|
||||
The invariant in this family is usually about the prefix you have already processed.
|
||||
|
||||
Typical invariant:
|
||||
|
||||
> "Everything before the write pointer is already in the correct final form."
|
||||
|
||||
That is what makes these problems feel different from opposite-direction scans. You are not searching from both sides. You are building the answer progressively as you sweep through the input.
|
||||
|
||||
### Subtypes in this section
|
||||
|
||||
#### 1. Compaction / Overwrite
|
||||
|
||||
The slow pointer marks the next output position. The fast pointer scans the input. When the current value should be kept, copy it forward and advance the slow pointer.
|
||||
|
||||

|
||||
|
||||
#### 2. Relative Speed
|
||||
|
||||
Both pointers move forward, but one moves twice as fast. The invariant is about their distance and how much of the structure each pointer has consumed.
|
||||
|
||||

|
||||
|
||||
#### 3. Fixed Gap
|
||||
|
||||
Both pointers move forward while preserving a gap. The invariant is not about a rewritten prefix. It is about the distance between the pointers.
|
||||
|
||||

|
||||
|
||||
### What changes across problems
|
||||
|
||||
The family stays the same, but the invariant changes slightly:
|
||||
|
||||
- in array compaction, the invariant is about the prefix being correct
|
||||
- in fast/slow midpoint problems, the invariant is about relative speed
|
||||
- in fixed-gap problems, the invariant is about distance
|
||||
|
||||
So the unifying idea is not "both pointers move right." The unifying idea is:
|
||||
|
||||
**"the structure to the left of the pointers already has a known meaning"**
|
||||
|
||||
### What to watch out for
|
||||
|
||||
Common mistakes:
|
||||
|
||||
- Updating the write pointer before copying the current value.
|
||||
- Forgetting exactly what region is already valid.
|
||||
- Losing track of whether the invariant is about a prefix, a gap, or relative speed.
|
||||
|
||||
As you go through the following problems, pay attention to one question:
|
||||
|
||||
**What does the region before the slow pointer mean right now?**
|
||||
|
||||
## Opposite Direction
|
||||
|
||||
Opposite-direction pointer problems place one pointer on the left and one on the right, then move inward.
|
||||
|
||||
The hard part is never the loop itself. **The hard part is justifying which pointer is safe to move.**
|
||||
|
||||
### When this family applies
|
||||
|
||||
Reach for opposite-direction pointers when the problem asks you to:
|
||||
|
||||
- the input is sorted, or can be made monotonic by sorting
|
||||
- you are comparing a left choice and a right choice
|
||||
- the problem has symmetry
|
||||
- moving one side lets you eliminate a set of impossible answers
|
||||
|
||||
### Core Invariant
|
||||
|
||||
The invariant in this family is usually:
|
||||
|
||||
> "All candidate answers outside the current `[left, right]` range have already been ruled out."
|
||||
|
||||
That means each move must come with a proof. If you move a pointer without knowing why the discarded cases cannot contain the optimum, the algorithm is just guessing.
|
||||
|
||||
### Subtypes in this section
|
||||
|
||||
#### 1. Monotonic sum elimination
|
||||
|
||||
|
||||
Because the array is sorted:
|
||||
|
||||
- if the sum is too small, moving the left pointer right is the only useful move
|
||||
- if the sum is too large, moving the right pointer left is the only useful move
|
||||
|
||||
Sorted order gives you a monotonic rule.
|
||||
|
||||

|
||||
|
||||
#### 2. Symmetry checking
|
||||
|
||||
Here the pointers compare mirrored positions. The movement rule is mostly "move both," except when you must skip punctuation or spaces.
|
||||
|
||||

|
||||
|
||||
#### 3. Bottleneck elimination
|
||||
|
||||
This one is important because it looks different from the other two. The key idea is that the shorter wall limits the area. If you keep that shorter wall and decrease the width, you cannot improve the answer. So it is safe to discard that wall.
|
||||
|
||||

|
||||
|
||||
### Why this is not a sliding window
|
||||
|
||||
Opposite-direction scans do not maintain an interval property like "this window is valid."
|
||||
|
||||
Instead, each step does one of these:
|
||||
|
||||
- rules out sums that are too small or too large
|
||||
- checks mirrored characters
|
||||
- discards a bottleneck boundary
|
||||
|
||||
The interval itself is just the remaining search space.
|
||||
|
||||
### What to watch out for
|
||||
|
||||
Common mistakes:
|
||||
|
||||
- Using two pointers on an unsorted pair problem without first creating monotonicity.
|
||||
- Moving the taller wall in Container With Most Water without an elimination proof.
|
||||
- Calling the method sliding window just because two boundaries exist.
|
||||
|
||||
As you read the problems in this family, keep asking:
|
||||
|
||||
**What exact set of candidate answers did this pointer move eliminate?**
|
||||
@@ -9,6 +9,35 @@ topics:
|
||||
- { name: "Doubly-Linked List", slug: "doubly-linked-list" }
|
||||
video: "7ABFKPK2hD4"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/87"
|
||||
companies:
|
||||
- { name: "NetApp", period: "Thirty Days", frequency: 100 }
|
||||
- { name: "Adobe", period: "Thirty Days", frequency: 83.5 }
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 69.6 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 36.7 }
|
||||
- { name: "TikTok", period: "Three Months", frequency: 100 }
|
||||
- { name: "Palo Alto Networks", period: "Three Months", frequency: 92.8 }
|
||||
- { name: "Apple", period: "Three Months", frequency: 88.5 }
|
||||
- { name: "Goldman Sachs", period: "Three Months", frequency: 66.3 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 53.2 }
|
||||
- { name: "Airwallex", period: "Six Months", frequency: 100 }
|
||||
- { name: "Docusign", period: "Six Months", frequency: 100 }
|
||||
- { name: "Moloco", period: "Six Months", frequency: 100 }
|
||||
- { name: "Nutanix", period: "Six Months", frequency: 100 }
|
||||
- { name: "Oracle", period: "Six Months", frequency: 100 }
|
||||
- { name: "Twitch", period: "Six Months", frequency: 100 }
|
||||
- { name: "Snap", period: "Six Months", frequency: 92.4 }
|
||||
- { name: "Qualcomm", period: "Six Months", frequency: 92 }
|
||||
- { name: "Nvidia", period: "Six Months", frequency: 91.1 }
|
||||
- { name: "ByteDance", period: "Six Months", frequency: 88.6 }
|
||||
- { name: "MongoDB", period: "Six Months", frequency: 87.7 }
|
||||
- { name: "Expedia", period: "Six Months", frequency: 87.5 }
|
||||
- { name: "eBay", period: "Six Months", frequency: 86.1 }
|
||||
- { name: "LinkedIn", period: "Six Months", frequency: 74.9 }
|
||||
- { name: "Visa", period: "Six Months", frequency: 69.7 }
|
||||
- { name: "Uber", period: "Six Months", frequency: 61.9 }
|
||||
- { name: "Salesforce", period: "Six Months", frequency: 50.2 }
|
||||
sidebar:
|
||||
label: 'LRU Cache'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -8,6 +8,17 @@ topics:
|
||||
- { name: "Recursion", slug: "recursion" }
|
||||
video: "wgFPrzTjm7s"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/88"
|
||||
companies:
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 95.8 }
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 83.1 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 72.8 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 67.9 }
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 63.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 61.3 }
|
||||
- { name: "EarnIn", period: "Six Months", frequency: 100 }
|
||||
- { name: "Jane Street", period: "Six Months", frequency: 100 }
|
||||
- { name: "Palo Alto Networks", period: "Six Months", frequency: 75.9 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 43.5 }
|
||||
sidebar:
|
||||
label: 'Add Two Numbers'
|
||||
badge: 'Medium'
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: '380. Insert Delete GetRandom O(1)'
|
||||
description: 'Implement the RandomizedSet class:'
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Design", slug: "design" }
|
||||
- { name: "Randomized", slug: "randomized" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/90"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Apple", period: "Three Months", frequency: 80.4 }
|
||||
- { name: "Google", period: "Three Months", frequency: 32.7 }
|
||||
- { name: "Axon", period: "Six Months", frequency: 100 }
|
||||
- { name: "Okta", period: "Six Months", frequency: 100 }
|
||||
- { name: "Quora", period: "Six Months", frequency: 100 }
|
||||
- { name: "SoFi", period: "Six Months", frequency: 100 }
|
||||
- { name: "Uber", period: "Six Months", frequency: 61.9 }
|
||||
- { name: "Yandex", period: "Six Months", frequency: 59.6 }
|
||||
- { name: "LinkedIn", period: "Six Months", frequency: 54.1 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 24.1 }
|
||||
sidebar:
|
||||
label: 'Insert Delete GetRandom O(1)'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must implement the functions of the class such that each function works in average O(1) time complexity.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: ``
|
||||
- Output: ``
|
||||
- Explanation: Input ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"] `[[]`, `[1]`, `[2]`, `[2]`, `[]`, `[1]`, `[2]`, []] Output [null, true, false, true, `2`, true, false, 2] Explanation RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); // Inserts `1` to the set. Returns true as `1` was inserted successfully. randomizedSet.remove(2); // Returns false as `2` does not exist in the set. randomizedSet.insert(2); // Inserts `2` to the set, returns true. Set now contains `[1,2]`. randomizedSet.getRandom(); // getRandom() should return either `1` or `2` randomly. randomizedSet.remove(1); // Removes `1` from the set, returns true. Set now contains `[2]`. randomizedSet.insert(2); // `2` was already in the set, so return false. randomizedSet.getRandom(); // Since `2` is the only number in the set, getRandom() will always return `2`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `-2^31 <= val <= 2^31 - 1`
|
||||
- At most 2 * 10^5 calls will be made to insert, remove, and getRandom.
|
||||
- There will be at least one element in the data structure when getRandom is called.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
from random import choice
|
||||
|
||||
|
||||
class RandomizedSet:
|
||||
def __init__(self):
|
||||
self.list = []
|
||||
self.dict = {}
|
||||
|
||||
def insert(self, val: int) -> bool:
|
||||
if val in self.dict:
|
||||
return False
|
||||
self.dict[val] = len(self.list)
|
||||
self.list.append(val)
|
||||
return True
|
||||
|
||||
def remove(self, val: int) -> bool:
|
||||
if val in self.dict:
|
||||
last, idx = self.list[-1], self.dict[val]
|
||||
self.list[idx], self.dict[last] = last, idx
|
||||
self.list.pop()
|
||||
del self.dict[val]
|
||||
return True
|
||||
return False
|
||||
|
||||
def getRandom(self) -> int:
|
||||
return choice(self.list)
|
||||
|
||||
|
||||
# Your RandomizedSet object will be instantiated and called as such:
|
||||
# obj = RandomizedSet()
|
||||
# param_1 = obj.insert(val)
|
||||
# param_2 = obj.remove(val)
|
||||
# param_3 = obj.getRandom()
|
||||
```
|
||||
@@ -9,6 +9,13 @@ topics:
|
||||
- { name: "Floyd's Cycle Finding Algorithm", slug: "floyds-cycle-finding-algorithm" }
|
||||
video: "gBTe7lFR3vc"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/79"
|
||||
companies:
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Nvidia", period: "Three Months", frequency: 87.7 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
sidebar:
|
||||
label: 'Linked List Cycle'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: '143. Reorder List'
|
||||
description: 'You are given the head of a singly linked-list. The list can be represented as:'
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Recursion", slug: "recursion" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/81"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 42.3 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 48.3 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 21.2 }
|
||||
sidebar:
|
||||
label: 'Reorder List'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `head = [1,2,3,4]`
|
||||
- Output: `[1,4,2,3]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `head = [1,2,3,4,5]`
|
||||
- Output: `[1,5,2,4,3]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the list is in the range [1, 5 * 10^4].
|
||||
- `1 <= Node.val <= 1000`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def reorderList(self, head: ListNode | None) -> None:
|
||||
# Find middle
|
||||
slow, fast = head, head.next
|
||||
while fast and fast.next:
|
||||
slow = slow.next
|
||||
fast = fast.next.next
|
||||
|
||||
# Reverse second half
|
||||
second = slow.next
|
||||
prev = slow.next = None
|
||||
while second:
|
||||
tmp = second.next
|
||||
second.next = prev
|
||||
prev = second
|
||||
second = tmp
|
||||
|
||||
# merge two halfs
|
||||
first, second = head, prev
|
||||
while second:
|
||||
tmp1, tmp2 = first.next, second.next
|
||||
first.next = second
|
||||
second.next = tmp1
|
||||
first, second = tmp1, tmp2
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: '19. Remove Nth Node From End of List'
|
||||
description: Given the head of a linked list, remove the n^th node from the end of the list and return its head
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/80"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 51.1 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 46.8 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 43.3 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 24.1 }
|
||||
sidebar:
|
||||
label: 'Remove Nth Node From End of List'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Could you do this in one pass?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `head = [1,2,3,4,5], n = 2`
|
||||
- Output: `[1,2,3,5]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `head = [1], n = 1`
|
||||
- Output: `[]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `head = [1,2], n = 1`
|
||||
- Output: `[1]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the list is sz.
|
||||
- `1 <= sz <= 30`
|
||||
- `0 <= Node.val <= 100`
|
||||
- `1 <= n <= sz`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def removeNthFromEnd(self, head: ListNode | None, n: int) -> ListNode | None:
|
||||
dummy = ListNode(0)
|
||||
dummy.next = head
|
||||
|
||||
fast = dummy
|
||||
for _ in range(n):
|
||||
if fast.next is None:
|
||||
return head
|
||||
fast = fast.next
|
||||
|
||||
slow = dummy
|
||||
while fast.next is not None:
|
||||
slow = slow.next
|
||||
fast = fast.next
|
||||
|
||||
slow.next = slow.next.next
|
||||
|
||||
return dummy.next
|
||||
```
|
||||
@@ -7,6 +7,16 @@ topics:
|
||||
- { name: "Recursion", slug: "recursion" }
|
||||
video: "G0_I-ZF0S38"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/77"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 50.2 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Nokia", period: "Three Months", frequency: 100 }
|
||||
- { name: "Infosys", period: "Three Months", frequency: 66 }
|
||||
- { name: "tcs", period: "Three Months", frequency: 57.2 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 78.4 }
|
||||
sidebar:
|
||||
label: 'Reverse Linked List'
|
||||
badge: 'Easy'
|
||||
|
||||
@@ -7,6 +7,13 @@ topics:
|
||||
- { name: "Recursion", slug: "recursion" }
|
||||
video: "XIdigk956u0"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/78"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 57.2 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 29 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Accenture", period: "Six Months", frequency: 75 }
|
||||
sidebar:
|
||||
label: 'Merge Two Sorted Lists'
|
||||
badge: 'Easy'
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: '82. Remove Duplicates from Sorted List II'
|
||||
description: You are given the head of a sorted linked list
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/84"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Three Months", frequency: 34.6 }
|
||||
- { name: "Google", period: "Three Months", frequency: 23.9 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 34.2 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 24.5 }
|
||||
sidebar:
|
||||
label: 'Remove Duplicates from Sorted List II'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `head = [1,2,3,3,4,4,5]`
|
||||
- Output: `[1,2,5]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `head = [1,1,1,2,3]`
|
||||
- Output: `[2,3]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the list is in the range [0, 300].
|
||||
- `-100 <= Node.val <= 100`
|
||||
- The list is guaranteed to be sorted in ascending order.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def deleteDuplicates(self, head: ListNode | None) -> ListNode | None:
|
||||
sentinel = ListNode(0, head)
|
||||
pred = sentinel
|
||||
|
||||
while head:
|
||||
if head.next and head.val == head.next.val:
|
||||
while head.next and head.val == head.next.val:
|
||||
head = head.next
|
||||
|
||||
pred.next = head.next
|
||||
else:
|
||||
pred = pred.next
|
||||
|
||||
head = head.next
|
||||
|
||||
return sentinel.next
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: '876. Middle of the Linked List'
|
||||
description: Given the head of a singly linked list, return the middle node of the linked list
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/82"
|
||||
companies:
|
||||
- { name: "Google", period: "Thirty Days", frequency: 50.2 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 49.8 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 39.1 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 60.1 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 37.9 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
sidebar:
|
||||
label: 'Middle of the Linked List'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `head = [1,2,3,4,5]`
|
||||
- Output: `[3,4,5]`
|
||||
- Explanation: The middle node of the list is node `3`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `head = [1,2,3,4,5,6]`
|
||||
- Output: `[4,5,6]`
|
||||
- Explanation: Since the list has two middle nodes with values `3` and `4`, we return the second one.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the list is in the range [1, 100].
|
||||
- `1 <= Node.val <= 100`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def middleNode(self, head: ListNode | None) -> ListNode | None:
|
||||
slow = fast = head
|
||||
while fast and fast.next:
|
||||
fast = fast.next.next
|
||||
slow = slow.next
|
||||
|
||||
return slow
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: '92. Reverse Linked List II'
|
||||
description: Given the head of a singly linked list and two integers left and right where left <= right, reverse the nodes of the list from position left to position right, and return the reversed list
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/83"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 83.1 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Google", period: "Three Months", frequency: 28.8 }
|
||||
sidebar:
|
||||
label: 'Reverse Linked List II'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Could you do it in one pass?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `head = [1,2,3,4,5], left = 2, right = 4`
|
||||
- Output: `[1,4,3,2,5]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `head = [5], left = 1, right = 1`
|
||||
- Output: `[5]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the list is n.
|
||||
- `1 <= n <= 500`
|
||||
- `-500 <= Node.val <= 500`
|
||||
- `1 <= left <= right <= n`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def reverseBetween(
|
||||
self, head: ListNode | None, left: int, right: int
|
||||
) -> ListNode | None:
|
||||
cur, prev = head, None
|
||||
while left > 1:
|
||||
prev = cur
|
||||
cur = cur.next
|
||||
left, right = left - 1, right - 1
|
||||
|
||||
tail, con = cur, prev
|
||||
while right:
|
||||
third = cur.next
|
||||
cur.next = prev
|
||||
prev = cur
|
||||
cur = third
|
||||
right -= 1
|
||||
|
||||
if con:
|
||||
con.next = prev
|
||||
else:
|
||||
head = prev
|
||||
|
||||
tail.next = cur
|
||||
return head
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: '36. Valid Sudoku'
|
||||
description: 'Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:'
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/94"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 48.9 }
|
||||
- { name: "Apple", period: "Three Months", frequency: 69 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 36.8 }
|
||||
- { name: "Google", period: "Three Months", frequency: 23.9 }
|
||||
- { name: "Confluent", period: "Six Months", frequency: 100 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 32.7 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 32 }
|
||||
sidebar:
|
||||
label: 'Valid Sudoku'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `board = [["5","3",".",".","7",".",".",".","."] ,["6",".",".","1","9","5",".",".","."] ,[".","9","8",".",".",".",".","6","."] ,["8",".",".",".","6",".",".",".","3"] ,["4",".",".","8",".","3",".",".","1"] ,["7",".",".",".","2",".",".",".","6"] ,[".","6",".",".",".",".","2","8","."] ,[".",".",".","4","1","9",".",".","5"] ,[".",".",".",".","8",".",".","7","9"]]`
|
||||
- Output: `true`
|
||||
|
||||
### Example 2:
|
||||
- Input: `board = [["8","3",".",".","7",".",".",".","."] ,["6",".",".","1","9","5",".",".","."] ,[".","9","8",".",".",".",".","6","."] ,["8",".",".",".","6",".",".",".","3"] ,["4",".",".","8",".","3",".",".","1"] ,["7",".",".",".","2",".",".",".","6"] ,[".","6",".",".",".",".","2","8","."] ,[".",".",".","4","1","9",".",".","5"] ,[".",".",".",".","8",".",".","7","9"]]`
|
||||
- Output: `false`
|
||||
- Explanation: Same as Example `1`, except with the `5` in the top left corner being modified to `8`. Since there are two 8's in the top left 3x3 sub-box, it is invalid.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `board.length == 9`
|
||||
- `board[i].length == 9`
|
||||
- board[i][j] is a digit 1-9 or '.'.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
import collections
|
||||
|
||||
|
||||
class Solution:
|
||||
def isValidSudoku(self, board: list[list[str]]) -> bool:
|
||||
cols = collections.defaultdict(set)
|
||||
rows = collections.defaultdict(set)
|
||||
squares = collections.defaultdict(set) # (r/3, c/3)
|
||||
|
||||
for r in range(9):
|
||||
for c in range(9):
|
||||
if board[r][c] == ".":
|
||||
continue
|
||||
if (
|
||||
board[r][c] in rows[r]
|
||||
or board[r][c] in cols[c]
|
||||
or board[r][c] in squares[(r // 3, c // 3)]
|
||||
):
|
||||
return False
|
||||
cols[c].add(board[r][c])
|
||||
rows[r].add(board[r][c])
|
||||
squares[(r // 3, c // 3)].add(board[r][c])
|
||||
|
||||
return True
|
||||
```
|
||||
@@ -8,6 +8,15 @@ topics:
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
video: "fMSJSS7eO1w"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/91"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 83.1 }
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "Google", period: "Three Months", frequency: 51.1 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 43.3 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 64.8 }
|
||||
- { name: "Capital One", period: "Six Months", frequency: 64.2 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 60.1 }
|
||||
sidebar:
|
||||
label: 'Rotate Image'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -8,6 +8,17 @@ topics:
|
||||
- { name: "Simulation", slug: "simulation" }
|
||||
video: "BJnMZNwUk1M"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/92"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
||||
- { name: "Microsoft", period: "Thirty Days", frequency: 57.2 }
|
||||
- { name: "Google", period: "Thirty Days", frequency: 36.7 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 46 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Capital One", period: "Six Months", frequency: 73.8 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 69.7 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 64.8 }
|
||||
- { name: "Infosys", period: "Six Months", frequency: 56.2 }
|
||||
- { name: "TikTok", period: "Six Months", frequency: 55.2 }
|
||||
sidebar:
|
||||
label: 'Spiral Matrix'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -8,6 +8,13 @@ topics:
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
video: "T41rL0L3Pnw"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/93"
|
||||
companies:
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 48.7 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 43.3 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 33.4 }
|
||||
- { name: "Google", period: "Three Months", frequency: 32.7 }
|
||||
- { name: "tcs", period: "Six Months", frequency: 43.5 }
|
||||
sidebar:
|
||||
label: 'Set Matrix Zeroes'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { defineMeta } from "blume";
|
||||
|
||||
export default defineMeta({ title: "2. Nodal & Grid", order: 2 });
|
||||
export default defineMeta({ title: "Nodal & Grid", order: 2 });
|
||||
+7
@@ -9,6 +9,13 @@ topics:
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "5LUXSvjmGCw"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/112"
|
||||
companies:
|
||||
- { name: "tcs", period: "Thirty Days", frequency: 74.3 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 33.4 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 28.9 }
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
- { name: "Uber", period: "Six Months", frequency: 71.5 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 24.5 }
|
||||
sidebar:
|
||||
label: 'Kth Smallest Element in a BST'
|
||||
badge: 'Medium'
|
||||
|
||||
+6
@@ -11,6 +11,12 @@ topics:
|
||||
- { name: "Lowest Common Ancestor", slug: "lowest-common-ancestor" }
|
||||
video: "gs2LMfuOR9k"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/113"
|
||||
companies:
|
||||
- { name: "BlackRock", period: "Thirty Days", frequency: 76.6 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 32 }
|
||||
- { name: "Amazon", period: "Six Months", frequency: 30.1 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
- { name: "Google", period: "Six Months", frequency: 21.9 }
|
||||
sidebar:
|
||||
label: 'Lowest Common Ancestor of a Binary Search Tree'
|
||||
badge: 'Medium'
|
||||
|
||||
+7
@@ -9,6 +9,13 @@ topics:
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "s6ATEkipzow"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/111"
|
||||
companies:
|
||||
- { name: "Ola Cabs", period: "Thirty Days", frequency: 76.7 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 46.8 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 34.6 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Google", period: "Three Months", frequency: 17.3 }
|
||||
sidebar:
|
||||
label: 'Validate Binary Search Tree'
|
||||
badge: 'Medium'
|
||||
|
||||
+12
@@ -10,6 +10,18 @@ topics:
|
||||
- { name: "Quickselect", slug: "quickselect" }
|
||||
video: "XEmy13g1Qxc"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/118"
|
||||
companies:
|
||||
- { name: "Meta", period: "Thirty Days", frequency: 70.8 }
|
||||
- { name: "Spotify", period: "Three Months", frequency: 90.3 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 36.8 }
|
||||
- { name: "Google", period: "Three Months", frequency: 32.7 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 28.9 }
|
||||
- { name: "Walmart Labs", period: "Six Months", frequency: 70.5 }
|
||||
- { name: "IBM", period: "Six Months", frequency: 70.1 }
|
||||
- { name: "LinkedIn", period: "Six Months", frequency: 63.3 }
|
||||
- { name: "Flipkart", period: "Six Months", frequency: 57.1 }
|
||||
- { name: "TikTok", period: "Six Months", frequency: 55.2 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 37.9 }
|
||||
sidebar:
|
||||
label: 'Kth Largest Element in an Array'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -10,6 +10,18 @@ topics:
|
||||
- { name: "Tournament Sort", slug: "tournament-sort" }
|
||||
video: "q5a5OiGbT6Q"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/120"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Thirty Days", frequency: 39.1 }
|
||||
- { name: "FreshWorks", period: "Three Months", frequency: 100 }
|
||||
- { name: "Snap", period: "Three Months", frequency: 100 }
|
||||
- { name: "Deloitte", period: "Three Months", frequency: 89.1 }
|
||||
- { name: "Salesforce", period: "Three Months", frequency: 64.7 }
|
||||
- { name: "Bloomberg", period: "Three Months", frequency: 54 }
|
||||
- { name: "Microsoft", period: "Three Months", frequency: 42.1 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Google", period: "Three Months", frequency: 23.9 }
|
||||
- { name: "Nvidia", period: "Six Months", frequency: 78.7 }
|
||||
- { name: "Apple", period: "Six Months", frequency: 57.9 }
|
||||
sidebar:
|
||||
label: 'Merge k Sorted Lists'
|
||||
badge: 'Hard'
|
||||
|
||||
+4
@@ -13,6 +13,10 @@ topics:
|
||||
- { name: "K-D Tree", slug: "k-d-tree" }
|
||||
video: "rI2EBUEMfTk"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/119"
|
||||
companies:
|
||||
- { name: "Amazon", period: "Three Months", frequency: 28.9 }
|
||||
- { name: "Meta", period: "Six Months", frequency: 47.3 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
sidebar:
|
||||
label: 'K Closest Points to Origin'
|
||||
badge: 'Medium'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { defineMeta } from "blume";
|
||||
|
||||
export default defineMeta({ title: "3. Hierarchical", order: 3 });
|
||||
export default defineMeta({ title: "Hierarchical", order: 3 });
|
||||
@@ -8,6 +8,12 @@ topics:
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "6ZnyEApgFYg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/106"
|
||||
companies:
|
||||
- { name: "Google", period: "Three Months", frequency: 41 }
|
||||
- { name: "Meta", period: "Three Months", frequency: 34.5 }
|
||||
- { name: "Amazon", period: "Three Months", frequency: 28.9 }
|
||||
- { name: "Microsoft", period: "Six Months", frequency: 32 }
|
||||
- { name: "Bloomberg", period: "Six Months", frequency: 25.5 }
|
||||
sidebar:
|
||||
label: 'Binary Tree Level Order Traversal'
|
||||
badge: 'Medium'
|
||||
|
||||
Loaded 100 of 159 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user