PatternsDynamic programming and advanced structures
Rust for interviews
When something goes wrong, you want to know whether it's the algorithm or the Rust. So first state the algorithm in plain words. Then pick ownership, number types, and collections that do exactly that.
| Need | Usual Rust type | Remember |
|---|---|---|
| State indexed by position | Vec<T> |
Indexes are usize; subtracting can underflow. |
| "Is it there?" or counts | HashSet or HashMap |
A lookup also costs building and hashing the key. |
| Keys in sorted order | BTreeSet or BTreeMap |
Use when you need ordered queries. |
| First in, first out (BFS) | VecDeque |
pop_front doesn't shift the other elements. |
| Last in, first out (stack) | Vec |
push and pop are the stack. |
| Changing maximum | BinaryHeap |
Iterating it doesn't give sorted order. |
| Changing minimum | BinaryHeap<Reverse<T>> |
With tuples, the later fields break ties. |
| Missing or unreachable | Option<T> |
Keeps "nothing" separate from a real zero. |
The standard library documentation describes each collection's guarantees and tradeoffs S13. For heap direction and ordering, see S10.
Use i64 for sums of i32 values when the limits call for it. Convert before adding or multiplying: an i32 product converted to i64 afterward has already overflowed. usize suits valid indexes, but not −1 as a "not found" marker. Ranges that include the start and exclude the end often remove the need for that marker.
Rust strings are UTF-8. If the input is promised to be ASCII, s.as_bytes() gives easy indexed characters. For Unicode code points, collect s.chars() if you need indexes; that allocates, and still doesn't match what a reader sees as one letter. Say which alphabet you assume instead of quietly treating bytes as characters S14.
Borrow checker errors often point to overlapping reads and writes. Copy a small value out of a map or heap before you change the collection. For counts, entry(key).or_insert(0) updates in place. Don't clone whole graphs or paths just to silence a borrow error, unless you count that cost.
Deep recursion in Rust can overflow the stack on a long chain. Your own stack gives more predictable memory for deep graph searches. LeetCode's tree problems in Rust use Rc<RefCell<TreeNode>>; for algorithms you control, adjacency lists indexed by number can be simpler. Neither changes why the algorithm is correct.
When testing, separate three things: it compiles, it passes the examples, and the algorithm is right. When you can, write a slow but obviously correct version for small inputs. Generate small cases, compare the answers, and study the smallest one that disagrees. Tests hunt for counterexamples; the reasoning about what stays true explains why every valid input works.