Practice LeetCode with Typst

Hi everyone! :wave:

I’m excited to share a new package I’ve been working on: leetcode - a practice workbook for solving LeetCode problems in Typst.

demo
demo2

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

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/while loops 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. :pray:


Note: Problem descriptions are the property of © LeetCode. This package is for personal learning and educational purposes only.


4 Likes