A linked list stores items in nodes, each holding a value and a reference to the next node. The list itself only remembers the first node, the head; you reach every other item by following next references:
class Node:
def __init__(self, value, next_node=None):
self.value = value
self.next = next_node
head = Node("Quay", Node("Market", Node("Hill"))) # Quay -> Market -> HillPython's built-in list is not a linked list; it's a dynamic array. You rarely need to write linked lists in Python applications, but they're a foundation for trees and graphs, a common interview topic, and a clear way to learn how references work. This lesson covers nodes, traversal, insertion, deletion and their real costs, using the stations on a tram line.
Nodes
A node is a small object with two attributes: the data and a reference to the next node, or None at the end:
class Node:
def __init__(self, value, next_node=None):
self.value = value
self.next = next_node
def __repr__(self):
return f"Node({self.value!r})"
hill = Node("Hill")
market = Node("Market", hill)
quay = Node("Quay", market)
print(quay.next.next) # Node('Hill')The nodes don't sit next to each other in memory; each one just knows where the next one is. That's the key difference from a Python list, which stores references in one contiguous block so it can jump straight to any index.
A doubly linked list adds a prev reference to each node, allowing traversal in both directions and O(1) removal of a node you already hold, at the cost of more memory and more references to keep consistent. collections.deque is implemented as a doubly linked list of blocks.
Head and traversal
The list is represented by its head. To visit every value, start at the head and follow next until you reach None:
def values(head):
current = head
while current is not None:
yield current.value
current = current.next
print(list(values(quay))) # ['Quay', 'Market', 'Hill']An empty list is simply head = None, and the loop handles it naturally by not running. Finding the length, searching for a value or reaching the k-th item all require walking from the head, so they're O(n).
If a bug makes a node point back to an earlier one, the list contains a cycle, and this loop never ends. Floyd's "tortoise and hare" algorithm detects cycles by moving one pointer one step and another two steps at a time; if they ever meet, there's a cycle. In your own code, keeping all relinking inside a few well-tested methods prevents cycles in the first place.
Insertion
Inserting into a linked list means relinking references, not moving elements:
def insert_after(node, value):
node.next = Node(value, node.next) # new node points to the old next
def push_front(head, value):
return Node(value, head) # new node becomes the headBoth operations are O(1): only a couple of references change, however long the list is. In a Python list, insert(0, x) is O(n) because every element shifts.
The catch is finding the right place. Inserting "Harbour" after "Market" first requires walking from the head to the Market node, which is O(n). Linked lists are fast to modify when you already hold a reference to the right node, as when processing nodes in order, and no faster than arrays otherwise. Order of assignment matters: set the new node's next before redirecting the predecessor, or you lose the rest of the list.
Deletion and costs
To delete a node, make its predecessor point past it. Deleting the head is a special case, because there's no predecessor: the head itself changes, so a function that might delete it must return the new head:
def delete(head, value):
if head is None:
return None
if head.value == value:
return head.next # new head
current = head
while current.next is not None:
if current.next.value == value:
current.next = current.next.next
break
current = current.next
return headAlways test the edge cases: an empty list, a one-node list, deleting the head, deleting the last node and deleting a value that isn't there.
| Operation | Linked list | Python list |
|---|---|---|
| Access by index | O(n) | O(1) |
| Insert/delete at front | O(1) | O(n) |
| Insert/delete after a held node | O(1) | O(n) |
| Append at end | O(n), or O(1) with a tail reference | O(1) amortised |
| Memory per item | node object + references | one reference |
For most Python programs, a list or deque is faster in practice because of better memory locality and C implementations.
Independent practice and a worked check
Task: implement head insertion and removal, and test empty and single-node station lists.
class Node:
def __init__(self, value, next_node=None):
self.value, self.next = value, next_node
def push(head, value):
return Node(value, head)
def pop(head):
if head is None:
raise IndexError("pop from empty line")
return head.value, head.next
head = None
head = push(head, "Hill")
value, head = pop(head)
print(value, head) # Hill None: back to empty
head = push(push(None, "Hill"), "Quay")
value, head = pop(head)
print(value, head.value) # Quay Hill
try:
pop(None)
except IndexError as error:
print(error) # pop from empty lineThese two functions make a linked-list stack: both operations are O(1). For the worked check, a singly linked list reaches the next value by following a next reference.
Reference and next practice
For background, see the collections.deque documentation, a production linked structure. Continue with hash tables and trees and binary search trees. Practise with the Python exercises and Python quiz.
Linked Lists Summary
- Nodes: A linked node stores a value and a reference to the next node.
- Head and traversal: The head identifies the first node and traversal follows next references.
- Insertion: Insertion can relink a known predecessor without moving a whole array of elements.
- Deletion and costs: Deletion changes references and must handle the head and empty cases.
Linked Lists Examples
Each example shows the code, the output it produces and what each part does.
Linked Lists in a small program
class Node:
def __init__(self, value, next_node=None):
self.value = value
self.next = next_node
head = Node("Quay", Node("Market", Node("Hill")))
current = head
while current is not None:
print(current.value)
current = current.nextQuay
Market
HillLine-by-line explanation
- Lines 1 to 4Each node stores a value and the next node, defaulting to
None. - Line 6Three nested constructor calls build Quay → Market → Hill.
- Lines 7 to 10Traversal follows
nextuntil it reachesNone.
A small linked list class with edge cases
class Node:
def __init__(self, value, next_node=None):
self.value, self.next = value, next_node
class TramLine:
def __init__(self, stations=()):
self.head = None
for station in reversed(stations):
self.head = Node(station, self.head)
def __iter__(self):
node = self.head
while node:
yield node.value
node = node.next
def insert_after(self, existing, new):
node = self.head
while node and node.value != existing:
node = node.next
if node is None:
raise ValueError(f"{existing} not on the line")
node.next = Node(new, node.next)
def remove(self, station):
if self.head and self.head.value == station:
self.head = self.head.next
return True
node = self.head
while node and node.next:
if node.next.value == station:
node.next = node.next.next
return True
node = node.next
return False
line = TramLine(["Quay", "Market", "Hill"])
line.insert_after("Market", "Library")
print(list(line))
print(line.remove("Quay"), line.remove("Hill"), line.remove("Zoo"))
print(list(line))
print(list(TramLine()), TramLine().remove("Quay"))['Quay', 'Market', 'Library', 'Hill']
True True False
['Market', 'Library']
[] FalseLine-by-line explanation
- Lines 8 to 9Building from the reversed list with head insertion keeps the original order.
- Lines 25 to 27Removing the head is handled first, because it changes
self.head. - Line 42An empty line iterates to nothing and removal simply reports
False.
Common Linked Lists Mistakes
Losing the rest of the list during insertion
new = Node("Library")
market.next = new # Hill is no longer reachable
new.next = market.next # points to itself: a cyclenew = Node("Library", market.next) # link to Hill first
market.next = newForgetting that deleting the head changes the list
def delete(head, value):
if head.value == value:
head = head.next # only changes the local variable
...def delete(head, value):
if head is not None and head.value == value:
return head.next # caller must use the returned head
...Linked Lists in Real Projects
Linked structures in real systems
Linked lists underpin LRU caches (a dictionary plus a doubly linked list gives O(1) lookups and O(1) "move to most recent"), memory allocators, undo histories in some editors, and the adjacency lists of graphs. Python's own collections.OrderedDict historically used a doubly linked list to track insertion order.
In everyday Python code you'll use list, deque and dictionaries instead. The value of linked lists is the reference-based thinking: once you're comfortable relinking nodes, trees and graphs follow naturally.
Linked Lists FAQ
Does Python have a built-in linked list?
Not a general one. The built-in list is a dynamic array. collections.deque is implemented as a doubly linked list of blocks and gives O(1) operations at both ends, which covers most cases where you'd want a linked list.
When should I use a linked list in Python?
Rarely in application code; list and deque are usually faster. Linked lists make sense when you hold references to nodes and need O(1) insertion or removal at those points, such as in an LRU cache, and they're essential for learning trees and graphs.
How do I reverse a linked list in Python?
Walk the list once, pointing each node's next at the previous node: prev = None; while head: head.next, prev, head = prev, head, head.next; then prev is the new head. It runs in O(n) time and O(1) extra space.
How do I detect a cycle in a linked list?
Use two pointers: one moves one node at a time, the other two. If there's a cycle, the fast pointer eventually meets the slow one; if the fast pointer reaches None, there's no cycle. This is Floyd's algorithm.
Why is accessing an element by index slow in a linked list?
Nodes aren't stored next to each other, so there's no way to jump to position k. You must follow next k times from the head, which is O(k).
Linked Lists Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
How does a singly linked list reach the next value?
Which review concern is valid for linked lists?
Linked Lists Quiz
2 questions to check what you learned.
How does a singly linked list reach the next value?
- By constant time arbitrary indexing
- By following a next reference
- Python ignores the distinction
Which statement correctly describes insertion?
- Insertion can relink a known predecessor without moving a whole array of elements.
- Only the example output matters, not the rule
- Every operation has the same behavior