Singly Linked Lists
You'll learn to
- -Explain how a singly linked list chains Node objects together via next references
- -Compare linked list insertion to array/list insertion in Big-O terms
- -Recognize the core tradeoff: O(1) head insertion versus no random access
Every structure built so far this tier - the stack, the circular-buffer queue, the hashmap's buckets - leaned on a Python list, a contiguous array under the hood. A linked list throws that away entirely and builds a chain of separate objects instead, trading random access for genuinely cheap insertion and removal at the ends.
The Node: value + next
A linked list is nothing more than a chain of these Node objects - each one points to the next, and the list itself only needs to remember where the chain starts (head) and, usually, where it ends (tail).
Why Head Insertion is O(1) Here but O(n) for a Python List
Inserting at the front of a Python list with list.insert(0, x) forces every existing element to shift one slot to the right - O(n), the exact same shifting cost as the naive queue's pop(0) from the previous module. A linked list's prepend touches only one new node and the existing head reference - two pointer assignments, full stop, regardless of how long the chain already is. That is O(1), always, no matter the list's current size.
The Tradeoff: No Random Access
The cost of that flexibility is that a linked list cannot jump directly to "the 500th element" the way arr[500] does on a Python list. Reaching any node other than the head (or the tail, if tracked) means walking the chain one next reference at a time from the head - an O(n) operation, even though the equivalent lookup on a Python list is O(1).
This is exactly why ordinary code defaults to a Python list, reserving a hand-built linked list for specific situations - implementing another structure's internals (a queue, a stack, an LRU cache's eviction order in the tier's capstone), or anywhere O(1) insertion and removal at both ends is required without ever needing random access.