mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-02 13:12:44 +00:00
docs(docs): add new problem documentation pages and source selector component
This commit is contained in:
1 parent
598688977a
commit
5627d0ca64
9 files changed
+321
-6
No files matched your search
@@ -142,11 +142,8 @@ export default defineConfig({
|
|||||||
sidebar: {
|
sidebar: {
|
||||||
display: "flat", // "flat" | "group" | "page"
|
display: "flat", // "flat" | "group" | "page"
|
||||||
},
|
},
|
||||||
// The glossary is no content folder any more, so it is pinned above the
|
// No `featured`: Blume renders it above the `Sidebar` slot, and the
|
||||||
// sidebar tree, on every page and breakpoint.
|
// content-source selector must sit above the pinned links, so
|
||||||
featured: [
|
// components/SourceSidebar.astro renders both (Changelog, Appendix).
|
||||||
{ label: "Changelog", href: "/changelog", icon: "clock" },
|
|
||||||
{ label: "Appendix", href: "/appendix", icon: "library" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -16,5 +16,8 @@ export default defineComponents({
|
|||||||
layout: {
|
layout: {
|
||||||
// Topic landing pages' "what links here": every problem pointing at the page.
|
// Topic landing pages' "what links here": every problem pointing at the page.
|
||||||
PageFooter: "./components/BacklinksList.astro",
|
PageFooter: "./components/BacklinksList.astro",
|
||||||
|
// Content-source selector + pinned links above the nav tree, scoped to the
|
||||||
|
// active source (sources.ts).
|
||||||
|
Sidebar: "./components/SourceSidebar.astro",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
/**
|
||||||
|
* The `Sidebar` slot (also the mobile drawer's nav, which defaults to it): a
|
||||||
|
* Fumadocs-style content-source selector, then the pinned links, then
|
||||||
|
* Blume's own nav tree scoped to the active source (`sources.ts`).
|
||||||
|
*
|
||||||
|
* The pinned links live here, not in `navigation.featured`: Blume renders
|
||||||
|
* featured links above this slot, so the selector could not sit over them.
|
||||||
|
*
|
||||||
|
* The selector is a `<details>` with Blume's `data-blume-dropdown` light
|
||||||
|
* dismiss (outside press, Escape, focus leaving), like its own NavSelector.
|
||||||
|
*/
|
||||||
|
import Icon from "blume/components/Icon.astro";
|
||||||
|
import { withBase, withMountedBase } from "blume/components/islands/base-path.ts";
|
||||||
|
import NavTree from "blume/components/layout/NavTree.astro";
|
||||||
|
import type { UIStrings } from "blume/core/i18n-ui.ts";
|
||||||
|
import { isUnderPath } from "blume/core/navigation.ts";
|
||||||
|
import type { NavNode } from "blume/core/types.ts";
|
||||||
|
import { SOURCES } from "../sources.ts";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
items: NavNode[];
|
||||||
|
currentRoute: string;
|
||||||
|
strings?: UIStrings["nav"];
|
||||||
|
ids?: Map<NavNode, string>;
|
||||||
|
fragmentBase?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PINNED = [
|
||||||
|
{ label: "Changelog", href: "/changelog", icon: "clock" },
|
||||||
|
{ label: "Appendix", href: "/appendix", icon: "library" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const { items, currentRoute, ...tree } = Astro.props;
|
||||||
|
|
||||||
|
const active =
|
||||||
|
SOURCES.filter((s) => isUnderPath(currentRoute, s.path)).sort(
|
||||||
|
(a, b) => b.path.length - a.path.length,
|
||||||
|
)[0] ?? SOURCES[0]!;
|
||||||
|
|
||||||
|
const claims = (node: NavNode, path: string) => {
|
||||||
|
const at = node.kind === "page" ? node.route : node.path;
|
||||||
|
return at !== undefined && isUnderPath(at, path);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The root source keeps every node no other source claims; any other source
|
||||||
|
// shows its folder's children, as if that folder were the content root.
|
||||||
|
const scoped =
|
||||||
|
active.path === "/"
|
||||||
|
? items.filter(
|
||||||
|
(node) => !SOURCES.some((s) => s.path !== "/" && claims(node, s.path)),
|
||||||
|
)
|
||||||
|
: items.flatMap((node) =>
|
||||||
|
node.kind === "group" && node.path === active.path ? node.children : [],
|
||||||
|
);
|
||||||
|
---
|
||||||
|
|
||||||
|
<details class="group relative mb-3" data-blume-dropdown>
|
||||||
|
<summary
|
||||||
|
aria-label={`Content source: ${active.label}`}
|
||||||
|
class="flex w-full cursor-pointer list-none items-center gap-2.5 rounded-xl border border-border bg-muted/50 p-1.5 pe-2.5 text-foreground text-sm transition-colors hover:bg-muted group-open:bg-muted [&::-webkit-details-marker]:hidden"
|
||||||
|
>
|
||||||
|
<span class="flex size-7 shrink-0 items-center justify-center rounded-lg border border-border bg-background">
|
||||||
|
<Icon name={active.icon} size={15} />
|
||||||
|
</span>
|
||||||
|
<span class="min-w-0 flex-1 truncate font-medium">{active.label}</span>
|
||||||
|
<Icon class="shrink-0 text-muted-foreground" name="chevrons-up-down" size={14} />
|
||||||
|
</summary>
|
||||||
|
<div
|
||||||
|
class="absolute inset-x-0 z-50 mt-1.5 rounded-xl border border-border bg-background p-1 shadow-xl"
|
||||||
|
data-blume-dropdown-panel
|
||||||
|
>
|
||||||
|
{
|
||||||
|
SOURCES.map((source) => (
|
||||||
|
<a
|
||||||
|
aria-current={source === active ? "true" : undefined}
|
||||||
|
class="flex items-start gap-2.5 rounded-lg p-2 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground"
|
||||||
|
href={withMountedBase(source.path)}
|
||||||
|
>
|
||||||
|
<Icon class="mt-0.5 shrink-0" name={source.icon} size={16} />
|
||||||
|
<span class="min-w-0 flex-1">
|
||||||
|
<span class="block font-medium">{source.label}</span>
|
||||||
|
<span class="block text-muted-foreground text-xs">{source.description}</span>
|
||||||
|
</span>
|
||||||
|
{source === active && <Icon class="mt-0.5 shrink-0" name="check" size={14} />}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<nav aria-label="Featured" class="mb-4 border-border border-b pb-4">
|
||||||
|
<ul class="m-0 list-none p-0">
|
||||||
|
{
|
||||||
|
PINNED.map((link) => (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
aria-current={currentRoute === link.href ? "page" : undefined}
|
||||||
|
class="flex items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
|
||||||
|
href={withBase(link.href)}
|
||||||
|
>
|
||||||
|
<Icon class="shrink-0" name={link.icon} size={14} />
|
||||||
|
<span class="flex-1 truncate">{link.label}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<NavTree {...tree} currentRoute={currentRoute} items={scoped} />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { installDropdownDismiss } from "blume/components/dropdown-dismiss.ts";
|
||||||
|
|
||||||
|
installDropdownDismiss();
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
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"
|
||||||
|
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)
|
||||||
|
```
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
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"
|
||||||
|
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:
|
||||||
|
```
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
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"
|
||||||
|
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:
|
||||||
|
```
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
title: System Design
|
||||||
|
description: Notes for system design interviews — how to scope a problem, size it, and pick the parts that carry the load.
|
||||||
|
icon: network
|
||||||
|
sidebar:
|
||||||
|
label: Overview
|
||||||
|
---
|
||||||
|
|
||||||
|
## How an interview runs
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Requirements">
|
||||||
|
Pin down the functional requirements (what the system does) and the non-functional ones (latency, availability, consistency, scale) before drawing anything.
|
||||||
|
</Step>
|
||||||
|
<Step title="Estimates">
|
||||||
|
Size the traffic, storage and bandwidth with back-of-the-envelope numbers, so later choices have a reason.
|
||||||
|
</Step>
|
||||||
|
<Step title="API and data model">
|
||||||
|
Define the endpoints and the entities they read and write.
|
||||||
|
</Step>
|
||||||
|
<Step title="High-level design">
|
||||||
|
Draw the request path end to end: clients, load balancers, services, caches, stores and queues.
|
||||||
|
</Step>
|
||||||
|
<Step title="Deep dives">
|
||||||
|
Take the hardest part (a hot key, fan-out, a consistency guarantee) and work through its tradeoffs.
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
@@ -29,6 +29,7 @@ export default [
|
|||||||
{ from: "/71", to: "/stack/simplify-path" },
|
{ from: "/71", to: "/stack/simplify-path" },
|
||||||
{ from: "/73", to: "/matrix-index-math/set-matrix-zeroes" },
|
{ from: "/73", to: "/matrix-index-math/set-matrix-zeroes" },
|
||||||
{ from: "/74", to: "/binary-search/search-a-2d-matrix" },
|
{ from: "/74", to: "/binary-search/search-a-2d-matrix" },
|
||||||
|
{ from: "/76", to: "/sliding-window/minimum-window-substring" },
|
||||||
{ from: "/78", to: "/backtracking/subsets" },
|
{ from: "/78", to: "/backtracking/subsets" },
|
||||||
{ from: "/80", to: "/two-pointers/remove-duplicates-from-sorted-array-ii" },
|
{ from: "/80", to: "/two-pointers/remove-duplicates-from-sorted-array-ii" },
|
||||||
{ from: "/84", to: "/monotonic-stack/largest-rectangle-in-histogram" },
|
{ from: "/84", to: "/monotonic-stack/largest-rectangle-in-histogram" },
|
||||||
@@ -61,6 +62,7 @@ export default [
|
|||||||
{ from: "/211", to: "/trie/design-add-and-search-words-data-structure" },
|
{ from: "/211", to: "/trie/design-add-and-search-words-data-structure" },
|
||||||
{ from: "/215", to: "/heap-priority-queue/kth-largest-element-in-an-array" },
|
{ from: "/215", to: "/heap-priority-queue/kth-largest-element-in-an-array" },
|
||||||
{ from: "/217", to: "/hash-table/contains-duplicate" },
|
{ from: "/217", to: "/hash-table/contains-duplicate" },
|
||||||
|
{ from: "/224", to: "/stack/basic-calculator" },
|
||||||
{ from: "/226", to: "/tree-dfs/invert-binary-tree" },
|
{ from: "/226", to: "/tree-dfs/invert-binary-tree" },
|
||||||
{ from: "/230", to: "/binary-search-tree/kth-smallest-element-in-a-bst" },
|
{ from: "/230", to: "/binary-search-tree/kth-smallest-element-in-a-bst" },
|
||||||
{ from: "/235", to: "/binary-search-tree/lowest-common-ancestor-of-a-binary-search-tree" },
|
{ from: "/235", to: "/binary-search-tree/lowest-common-ancestor-of-a-binary-search-tree" },
|
||||||
@@ -89,6 +91,7 @@ export default [
|
|||||||
{ from: "/739", to: "/monotonic-stack/daily-temperatures" },
|
{ from: "/739", to: "/monotonic-stack/daily-temperatures" },
|
||||||
{ from: "/853", to: "/monotonic-stack/car-fleet" },
|
{ from: "/853", to: "/monotonic-stack/car-fleet" },
|
||||||
{ from: "/875", to: "/binary-search/koko-eating-bananas" },
|
{ from: "/875", to: "/binary-search/koko-eating-bananas" },
|
||||||
|
{ from: "/901", to: "/monotonic-stack/online-stock-span" },
|
||||||
{ from: "/973", to: "/heap-priority-queue/k-closest-points-to-origin" },
|
{ from: "/973", to: "/heap-priority-queue/k-closest-points-to-origin" },
|
||||||
{ from: "/974", to: "/prefix-sum/subarray-sums-divisible-by-k" },
|
{ from: "/974", to: "/prefix-sum/subarray-sums-divisible-by-k" },
|
||||||
{ from: "/977", to: "/two-pointers/squares-of-a-sorted-array" },
|
{ from: "/977", to: "/two-pointers/squares-of-a-sorted-array" },
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* The site's content sources — what the sidebar's source selector switches
|
||||||
|
* between (`components/SourceSidebar.astro`), Fumadocs' "root folders". A
|
||||||
|
* source owns every route under its `path`, and the sidebar shows only the
|
||||||
|
* active source's pages. The source at `/` owns whatever no other source
|
||||||
|
* claims, which is how the LeetCode campaign keeps its routes (`/tree-dfs/…`)
|
||||||
|
* while a later source mounts beside it.
|
||||||
|
*
|
||||||
|
* Adding one: a content folder at the path (`content/system-design/`) with an
|
||||||
|
* `index.mdx`, so the selector's link is a page, plus a row here. The active
|
||||||
|
* source is the deepest `path` the current route sits under.
|
||||||
|
*/
|
||||||
|
export interface Source {
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
/** A Blume built-in icon name. */
|
||||||
|
icon: string;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SOURCES: Source[] = [
|
||||||
|
{
|
||||||
|
label: "LeetCode",
|
||||||
|
description: "Data structures & algorithms",
|
||||||
|
icon: "binary",
|
||||||
|
path: "/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "System Design",
|
||||||
|
description: "Distributed systems interviews",
|
||||||
|
icon: "network",
|
||||||
|
path: "/system-design",
|
||||||
|
},
|
||||||
|
];
|
||||||
Reference in new issue
Block a user