A Typst package for solving LeetCode problems with live feedback – write your solution and instantly see test results as you type. Built-in test cases included!

Features
- Live Feedback: Write, compile, instantly see pass/fail – like a coding notebook
- Zero Setup: Import and start solving – built-in test cases for all problems
- Beautiful Output: Automatic formatting with professional PDF rendering
- Test Visualization: Side-by-side comparison of your output vs. expected results
- 69 Problems: Curated collection of classic LeetCode problems
- Extensible: Custom comparators, chessboard rendering, and more
Installation
#import "@preview/leetcode-livebook:0.1.0": problem, test
Quick Start
Create a new .typ file:
#import "@preview/leetcode-livebook:0.1.0": problem, test
// Display the problem
#problem(1)
// Write your solution
#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)
}
none
}
// Test with built-in test cases!
#test(1, solution)
If you want to display your solution, you can try
#import "@preview/leetcode-livebook:0.1.0": solve
#solve(1, ```typc
let solution(nums, target) = {
let d = (:)
let ans = (-1, -1)
for (i, num) in nums.enumerate() {
if str(target - num) in d {
return (d.at(str(target - num)), i)
}
d.insert(str(num), i)
}
ans
}
```)
- The main function name needs to be
solutionwhen using thesolve()API.
Compile:
typst compile my-solution.typ
Or live preview:
typst watch my-solution.typ
Available Problems
| ID | Title | Difficulty |
|---|---|---|
| 1 | Two Sum | Easy |
| 2 | Add Two Numbers | Medium |
| 3 | Longest Substring Without Repeating Characters | Medium |
| 4 | Median of Two Sorted Arrays | Hard |
| 5 | Longest Palindromic Substring | Medium |
| 6 | Zigzag Conversion | Medium |
| 7 | Reverse Integer | Medium |
| 8 | String to Integer (atoi) | Medium |
| 9 | Palindrome Number | Easy |
| 10 | Regular Expression Matching | Hard |
| 11 | Container With Most Water | Medium |
| 12 | Integer to Roman | Medium |
| 13 | Roman to Integer | Easy |
| 14 | Longest Common Prefix | Easy |
| 15 | 3Sum | Medium |
| 16 | 3Sum Closest | Medium |
| 17 | Letter Combinations of a Phone Number | Medium |
| 18 | 4Sum | Medium |
| 19 | Remove Nth Node From End of List | Medium |
| 20 | Valid Parentheses | Easy |
| 21 | Merge Two Sorted Lists | Easy |
| 22 | Generate Parentheses | Medium |
| 23 | Merge k Sorted Lists | Hard |
| 24 | Swap Nodes in Pairs | Medium |
| 25 | Reverse Nodes in k-Group | Hard |
| 26 | Remove Duplicates from Sorted Array | Easy |
| 33 | Search in Rotated Sorted Array | Medium |
| 35 | Search Insert Position | Easy |
| 39 | Combination Sum | Medium |
| 42 | Trapping Rain Water | Hard |
| 46 | Permutations | Medium |
| 48 | Rotate Image | Medium |
| 49 | Group Anagrams | Medium |
| 50 | Pow(x, n) | Medium |
| 51 | N-Queens | Hard |
| 53 | Maximum Subarray | Medium |
| 54 | Spiral Matrix | Medium |
| 55 | Jump Game | Medium |
| 56 | Merge Intervals | Medium |
| 62 | Unique Paths | Medium |
| 69 | Sqrt(x) | Easy |
| 70 | Climbing Stairs | Easy |
| 72 | Edit Distance | Medium |
| 76 | Minimum Window Substring | Hard |
| 78 | Subsets | Medium |
| 94 | Binary Tree Inorder Traversal | Easy |
| 101 | Symmetric Tree | Easy |
| 104 | Maximum Depth of Binary Tree | Easy |
| 110 | Balanced Binary Tree | Easy |
| 112 | Path Sum | Easy |
| 113 | Path Sum II | Medium |
| 116 | Populating Next Right Pointers in Each Node | Medium |
| 121 | Best Time to Buy and Sell Stock | Easy |
| 144 | Binary Tree Preorder Traversal | Easy |
| 145 | Binary Tree Postorder Traversal | Easy |
| 155 | Min Stack | Medium |
| 200 | Number of Islands | Medium |
| 206 | Reverse Linked List | Easy |
| 207 | Course Schedule | Medium |
| 209 | Minimum Size Subarray Sum | Medium |
| 210 | Course Schedule II | Medium |
| 218 | The Skyline Problem | Hard |
| 289 | Game of Life | Medium |
| 347 | Top K Frequent Elements | Medium |
| 407 | Trapping Rain Water II | Hard |
| 547 | Number of Provinces | Medium |
| 785 | Is Graph Bipartite? | Medium |
| 814 | Binary Tree Pruning | Medium |
| 997 | Find the Town Judge | Easy |
Multiple Problems in One File
Solve multiple problems by reusing the solution name:
#import "@preview/leetcode-livebook:0.1.0": problem, test
// Problem 1
#problem(1)
#let solution(nums, target) = { /* ... */ }
#test(1, solution)
// Problem 2
#pagebreak()
#problem(2)
#let solution(l1, l2) = { /* ... */ }
#test(2, solution)
Advanced Usage
Adding Extra Test Cases
Add your own test cases on top of built-in ones:
#import "@preview/leetcode-livebook:0.1.0": problem, test
#problem(1)
#let solution(nums, target) = { /* ... */ }
// Built-in cases + your extra cases
#test(1, solution, extra-cases: (
(nums: (99, 1), target: 100),
(nums: range(1000), target: 999),
))
Using Only Custom Test Cases
Override built-in cases completely:
#test(1, solution,
extra-cases: (
(nums: (1, 2, 3), target: 5),
),
default-cases: false)
Unordered Output
For problems with “return answer in any order”:
#import "@preview/leetcode-livebook:0.1.0": problem, test
#problem(15) // 3Sum
#let solution(nums) = { /* ... */ }
// Metadata is handled automatically!
#test(15, solution) // Uses unordered-compare automatically
Chessboard Rendering
For problems like N-Queens:
#import "@preview/leetcode-livebook:0.1.0": problem, test
#problem(51)
#let solution(n) = { /* ... */ }
// Chessboard rendering enabled automatically!
#test(51, solution)
Viewing Reference Solutions
Stuck on a problem? View the reference solution code:
#import "@preview/leetcode-livebook:0.1.0": problem, answer
#problem(1)
// ... tried but couldn't solve it ...
// Display the reference solution code
#answer(1)
Manual Reference Access
For advanced control:
#import "@preview/leetcode-livebook:0.1.0": problem, get-test-cases, get-problem-path
// Get built-in test cases
#let cases = get-test-cases(1)
// Get problem directory path
#let path = get-problem-path(1)
#import (path + "solution.typ"): solution
API Reference
Main Functions
| Function | Description |
|---|---|
problem(id) |
Display problem with difficulty badge |
test(id, fn, ...) |
Test solution with built-in cases |
answer(id) |
Display reference solution code |
solve(id, code-block) |
Display code and test in one call |
get-test-cases(id) |
Get built-in test cases as array |
get-problem-path(id) |
Get problem directory path |
get-problem-info(id) |
Get metadata (title, difficulty, labels) |
Data Structure Functions
| Function | Description |
|---|---|
linkedlist(array) |
Create linked list from array |
ll-val(list, id) |
Get value at node ID |
ll-next(list, id) |
Get next node ID |
ll-values(list) |
Get all values as array |
binarytree(array) |
Create binary tree from level-order array |
Utility Functions
| Function | Description |
|---|---|
display(value) |
Format value for output |
fill(value, n) |
Create array with n copies |
chessboard(board) |
Render chessboard visualization |
unordered-compare(a, b) |
Compare ignoring order |
float-compare(a, b) |
Compare floats with tolerance |
testcases(fn, ref, cases, ...) |
Manual test comparison |
Examples
Check out template/main.typ for a starter template that demonstrates the practice workbook mode with the solve() API.
Architecture
Each problem is organized as:
problems/XXXX/
├── problem.toml # Metadata (title, difficulty, labels)
├── description.typ # Problem statement
├── solution.typ # Reference solution
└── testcases.typ # Test cases (pure data)
problem.toml structure:
title = "Two Sum"
difficulty = "easy"
labels = ["array", "hash-table"]
# Optional: for special handling
# comparator = "unordered-compare"
# render-chessboard = true
Testcases can use helper functions:
// testcases.typ
#import "../../helpers.typ": linkedlist
#let cases = (
(head: linkedlist((1, 2, 3)), n: 2),
)
This design ensures:
- High cohesion: All files for a problem are together
- Built-in tests: No need to write test cases
- Metadata support: Difficulty badges, comparators, and rendering options
- Helper access: Testcases can use linkedlist, fill, etc.
Tips
Linked Lists
#import "@preview/leetcode-livebook:0.1.0": linkedlist, ll-values
#let list = linkedlist((1, 2, 3))
#let values = ll-values(list) // (1, 2, 3)
Binary Trees
#import "@preview/leetcode-livebook:0.1.0": binarytree
// Level-order array: [1, 2, 3, null, 4]
#let tree = binarytree((1, 2, 3, none, 4))
Live Preview
Use Typst’s watch mode or Tinymist for instant feedback.
Adding Your Own Test Cases
Mix built-in and custom cases:
#test(1, solution, extra-cases: (
(nums: (0, 0), target: 0),
(nums: range(100), target: 99),
))
Why Typst?
This project explores Typst as a functional programming language for algorithms, demonstrating:
- Expressive syntax for pattern matching and FP
- Beautiful output with minimal effort
- Rapid feedback with live preview
- Creative constraints that enhance problem-solving
Contributing
Maintained at github.com/lucifer1004/leetcode.typ.
To add problems, use:
python3 scripts/create.py <problem-id>
License
MIT License - see LICENSE file for details.
Disclaimer
LeetCode problem descriptions are the property of © LeetCode. This package is for personal learning and educational purposes only. For official problem statements, please visit leetcode.com.