Quick Answer

A linked list stores each value in a node holding a reference to the next node. Insertion at the front is instant, but reaching item n takes n steps. In Python a built-in list is usually better in practice — linked lists matter for interviews and for understanding pointers.

It is just nodes pointing at nodes

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

That is the entire idea. Each node holds a value and a reference to the next node. The last node's next is None, which is how you know you have reached the end.

Unlike a Python list, the nodes are not stored side by side in memory. They can be anywhere; the chain is held together by the references. That single difference explains every performance property that follows.

Building the list

class LinkedList:
    def __init__(self):
        self.head = None

    def append(self, data):
        n = Node(data)
        if not self.head:
            self.head = n
            return
        cur = self.head
        while cur.next:
            cur = cur.next
        cur.next = n

    def to_list(self):
        out, cur = [], self.head
        while cur:
            out.append(cur.data)
            cur = cur.next
        return out

ll = LinkedList()
for v in [10, 20, 30]:
    ll.append(v)
print(ll.to_list())    # [10, 20, 30]

Note what append costs. To add at the end it walks the entire list first, which is O(n). Adding at the front would be O(1) — create a node, point it at the current head, done. That asymmetry is the whole character of a linked list.

The if not self.head branch is the empty-list case, and forgetting it is the single most common bug when writing this from memory in an interview.

Reversal: the problem worth knowing cold

This appears in interviews constantly. The technique is three pointers moving in step:

def reverse(self):
    prev, cur = None, self.head
    while cur:
        nxt = cur.next      # remember where we were going
        cur.next = prev     # flip this link backwards
        prev, cur = cur, nxt  # step both forward
    self.head = prev

ll.reverse()
print(ll.to_list())    # [30, 20, 10]

The line that must come first is nxt = cur.next. The moment you write cur.next = prev, the forward link is gone — if you have not saved it, the rest of the list is unreachable and lost. Interviewers watch for exactly that ordering.

Then self.head = prev, not cur. When the loop ends cur is None, and prev is sitting on the last real node, which is the new head. Getting this wrong produces an empty list, which is the second most common mistake.

Linked list versus Python's built-in list

Honestly compared:

  • Access by index — Python list O(1), linked list O(n). You must walk from the head.
  • Insert at front — Python list O(n) because everything shifts, linked list O(1).
  • Insert at end — Python list O(1) amortised, linked list O(n) unless you keep a tail pointer.
  • Memory — linked list uses more, since every value carries a reference alongside it.
  • Cache behaviour — contiguous arrays are dramatically faster in practice, which is why real code rarely uses linked lists.

For everyday Python, use the built-in list. If you need fast insertion at both ends, use collections.deque, which is implemented for exactly that. Writing your own linked list in production Python is almost always the wrong call — and saying so in an interview, after implementing it correctly, is a good answer.

The related problems that keep appearing

Once reversal makes sense, these become approachable, and they share techniques:

  • Find the middle — two pointers, one moving twice as fast. When the fast one reaches the end, the slow one is at the middle.
  • Detect a cycle — the same two-speed idea. If the pointers ever meet, there is a loop.
  • Remove the nth node from the end — two pointers separated by n.
  • Merge two sorted lists — the merge step from merge sort.

Almost all of them are two pointers moving at different speeds or distances. Learn that pattern and this whole category becomes one idea rather than four. See the time complexity cheat sheet for how to talk about their cost.

Frequently Asked Questions

Why learn linked lists if Python has lists? For interviews, and for understanding references and memory. The pointer-manipulation skills transfer to trees and graphs, which do appear in real code far more often than linked lists do.
What is the difference between singly and doubly linked lists? A doubly linked list also stores a reference to the previous node, so you can walk backwards and delete a node you have a reference to in constant time. The cost is an extra reference per node and more links to keep correct.
Why is reversal asked so often in interviews? It is short enough for a whiteboard, has an unforgiving ordering requirement, and immediately reveals whether you understand references. Getting the save-then-flip order wrong is very visible.
Should I use collections.deque instead? For real code, yes, when you need fast appends and pops at both ends. It is implemented efficiently and is the practical answer whenever someone reaches for a linked list in Python.
How do I detect a loop in a linked list? Use two pointers, one advancing one node at a time and the other two. If the list has a cycle they eventually meet; if it does not, the fast pointer reaches the end. This is known as the tortoise and hare technique.