Hi everyone! ![]()
I’m excited to share a new package I’ve been working on: leetcode - a practice workbook for solving LeetCode problems in Typst.


What it does
- 69 built-in problems with descriptions, test cases, and reference solutions
- Auto-testing framework that validates your solutions against expected outputs
- Beautiful visualizations rendered entirely in Typst (no external dependencies!)
- Practice mode where you can write and test your own solutions
- Flexible filtering by difficulty, labels, or problem ID range
Algorithm Coverage
The problems cover a wide range of classic algorithms and data structures:
| Category | Topics |
|---|---|
| Data Structures | Array, String, Linked List, Binary Tree, Stack, Graph, Matrix, Heap, Trie |
| Algorithms | Dynamic Programming, Backtracking, Recursion, Binary Search, Sorting, DFS, BFS |
| Techniques | Two Pointers, Sliding Window, Divide & Conquer, Greedy, Union-Find, Topological Sort |
Showcase: Typst Visualization Capabilities
One of the highlights is the custom visualizations, demonstrating what Typst can do beyond documents:
| The Skyline Problem | Trapping Rain Water II |
|---|---|
| Gradient sky, colored buildings, red contour | 3D isometric terrain with water blocks |
| Trapping Rain Water | Container With Most Water |
|---|---|
| Gradient terrain with water accumulation | Two-pointer solution visualization |
All visualizations are pure Typst - no images, no plugins, just curve, polygon, place, and gradients!
Quick Start
// After merged
// #import "@preview/leetcode:0.1.0": conf, solve
//
// For now
#import "lib.typ": conf, solve
#show: conf.with(practice: true)
#solve(1, code-block: ```typc
let solution(nums, target) = {
let d = (:)
for (i, num) in nums.enumerate() {
if str(target - num) in d {
return (d.at(str(target - num)), i)
}
d.insert(str(num), i)
}
}
```)
Links
PR: typst/packages#3770
Repository: github.com/lucifer1004/leetcode.typ
Demo Site: lucifer1004.github.io/leetcode.typ
Sample PDF: Download
Algorithm Design in Typst
Implementing algorithms in Typst requires a different mindset due to its functional nature and scoping rules:
1. Closures Can’t Mutate Captured Variables
In languages like Python, you’d write:
result = []
def backtrack(path):
result.append(path) # Mutate outer variable
In Typst, closures capture variables by value. The workaround:
- Return values instead of mutating: Pass state through function returns
- Do mutations in loops:
for/whileloops allow mutation of outer variables
2. No In-Place Heap Operations
Passing a heap to a function copies it. Solution: inline heap operations within loops (see Problem 23: Merge k Sorted Lists).
3. Union-Find Pattern
// find() returns (root, path) — READ-ONLY
let find(parent, x) = { ... (root, path) }
for edge in edges {
let (root, path) = find(parent, x)
// Modify IN THE LOOP, not in the function
for node in path { parent.at(node) = root }
}
These constraints lead to cleaner, more functional code — and serve as an interesting exercise in algorithm design!
Why this package?
Beyond LeetCode practice, this package serves as a showcase of Typst’s capabilities:
- Complex algorithm implementation in pure Typst
- Custom data structures (linked lists, binary trees, priority queues)
- Advanced visualizations without external tools
- Document automation with filtering and testing
I hope this inspires more creative uses of Typst! Feedback and suggestions are welcome. ![]()
Note: Problem descriptions are the property of © LeetCode. This package is for personal learning and educational purposes only.



