File: remove-linked-list-elements/solution.py

Date: 2026-06-06

Time: 18:47

Purpose

This file is a self-contained solution to LeetCode #203 — Remove Linked List Elements. It owns three responsibilities: defining the linked list data structure (ListNode), implementing the removal algorithm (removeelements), and providing conversion helpers (tolist, from_list) plus a full test suite. In the broader leetcode-implementations repo, it follows the standard per-problem directory layout where solution.py holds both implementation and tests.

Key Components

ListNode

A minimal singly-linked list node. Two fields: val: int and next: Optional[ListNode]. The default constructor (val=0, next=None) makes it usable as a dummy/sentinel without arguments.

remove_elements(head, val) -> Optional[ListNode]

The core algorithm. Removes all nodes whose .val equals val and returns the (possibly new) head. Operates in-place by relinking pointers — no new nodes are allocated.

from_list(vals) -> Optional[ListNode]

Constructs a linked list from a Python list. Uses the same dummy-head pattern as the main algorithm.

to_list(head) -> list[int]

Linearizes a linked list back into a Python list for assertion-friendly comparison.

TestRemoveElements

Nine test cases covering: standard removal, empty list, all-matching, head removal, tail removal, consecutive duplicates, single node (match and no-match), and no matches.

Patterns

Dummy head (sentinel node). The central idiom. removeelements creates a ListNode(next=head) and walks from it, so deletions at the head position don't require special-casing — the loop body is uniform for every position. fromlist uses the same pattern for uniform list construction.

Two-pointer skip. curr always points to the node *before* the candidate for deletion (curr.next). When a match is found, curr.next is relinked to curr.next.next, effectively skipping the target node. When there's no match, curr advances. This is important: on a match, curr does not advance, because the new curr.next might also need removal.

Test helper check. A single method wraps the fromlist → removeelements → to_list pipeline so each test case is one line. This keeps tests declarative — input list, target value, expected output.

Dependencies

Imports: Only stdlib — unittest, typing.Optional, and from _future_ import annotations (for PEP 604-style forward references in the ListNode type annotation).

Imported by: Hundreds of test files across the repo import from this file (see Imported By list). This is surprising — ListNode, fromlist, and tolist appear to serve as the repo-wide linked list utilities, reused by every problem that involves linked lists. This makes this file a de facto shared dependency, even though it lives under a single problem's directory.

Flow

1. from_list converts a Python list into a ListNode chain.

2. remove_elements creates a dummy node pointing to head.

3. The while loop inspects curr.next on each iteration:

4. Loop terminates when curr.next is None (end of list).

5. dummy.next is returned — this correctly handles the case where the original head was removed.

Invariants

Error Handling

None. The function trusts its inputs (valid linked list, integer val). None head is handled implicitly — the while loop body never executes, and dummy.next (which is None) is returned. This is appropriate for a LeetCode solution operating within a controlled input contract.

Topics to Explore

Beliefs