What is a linked list?
A linked list is a linear data structure made of nodes, where each node stores a value and a pointer to the next node, so the elements can sit anywhere in memory. Inserting or deleting next to a node you already hold is O(1) pointer surgery, but reaching the i-th element is O(n), because the only way in is to follow the pointers from the head.
How it works
- Each node holds
dataand anextpointer. The list itself only storeshead, the address of the first node (plustailif it keeps one). - The last node's
nextis NULL, which marks the end. An empty list is simplyhead = NULL. - Traverse: start with
curr = headand repeatcurr = curr.nextuntilcurris NULL. Search, length and printing all work this way. - Insert after a node: allocate the new node, set
new.next = curr.nextfirst, thencurr.next = new. Inserting at the head is the same move withheadin place ofcurr.next. - Delete after a node: set
prev.next = prev.next.nextand free the unlinked node. Deleting the head just movesheadone node forward.
Linked list vs array
An array keeps its elements in one contiguous block, so element i lives at base + i × size: O(1) access. A linked list gives that up — its nodes are allocated one at a time wherever memory is free, tied together only by next pointers — and gets cheap structural change in return. Once you stand on the right node, inserting or deleting rewires two pointers and moves nothing else, and the list never needs resizing.
- Access by index: array O(1), linked list O(n).
- Insert or delete at the front: array O(n) (everything shifts), linked list O(1).
- Insert or delete in the middle: both need O(n) to reach the spot; after that the list is O(1) and the array O(n).
- Memory and speed: every node also stores one or two pointers (8 bytes each on a 64-bit machine), and scanning scattered nodes misses the CPU cache — so lists are often slower in practice even when the big-O matches.
Singly, doubly and circular linked lists
The three classic variants differ only in which pointers each node carries, but that decides which operations are cheap.
- Singly linked list —
[data | next]. The lightest, and forward-only. Deleting a node needs its predecessor, so deleting the tail is O(n) even with atailpointer. - Doubly linked list —
[prev | data | next]. One more pointer per node buys backward traversal and O(1) deletion of any node you hold; with the usualheadandtailpointers both ends are O(1). Inserts write four pointers instead of two. - Circular linked list — the last node points back to the head. A pointer to the last node reaches both ends in O(1), which suits round-robin scheduling; loops stop when they return to the head.
Use singly for a stack, or a queue with a tail pointer; doubly when you remove arbitrary nodes or walk both ways (an LRU cache); circular when the data really is a ring. The List type switch above converts the list on screen so you can compare them.
Worked example: insert 99 at index 2
Take the list the visualizer opens with: 12 → 7 → 33 → 5 → 21 → 48 → NULL. To put 99 at index 2 you need the node before that position — index 1, value 7 — so walk one step from the head: curr = head (12), then curr = curr.next (7).
Now two pointer writes, in this order:
new.next = curr.next— 99 now points at 33. Nothing in the list has changed yet; 99 is just aiming at the right place.curr.next = new— 7 now points at 99, and the list reads12 → 7 → 99 → 33 → 5 → 21 → 48.
The order is the whole trick. Do the second write first and 7.next is overwritten before anyone saved the address of 33: the nodes 33, 5, 21 and 48 become unreachable — a memory leak in C, silently lost data everywhere else. The Wrong order button in the visualizer plays exactly that mistake so you can watch the rest of the list fall away.
Common linked list mistakes
- Losing the rest of the list by overwriting a
nextpointer before saving it. - Dereferencing NULL — reading
curr.next.dataon the last node, or running any operation on an empty list. - Forgetting the head or tail pointer when the node you insert or delete sits at either end. Test with 0, 1 and 2 nodes.
- Off-by-one walks — to insert at index i, stop on node i − 1, not on node i.
A dummy (sentinel) node in front of the real head gives every real node a predecessor, so insert and delete need no if index == 0 branch.
Where linked lists are used
Linked lists rarely beat a dynamic array for plain storage, but they shine when elements must be spliced in and out without moving anything else:
- LRU caches pair a hash map with a doubly linked list, so "move to front" and "evict the oldest" are both O(1).
- Stacks and queues — a singly linked list with head (and tail) pointers gives O(1) push, pop and enqueue with no resizing.
- Graph adjacency lists, hash-table buckets with separate chaining, and the free lists inside memory allocators.
- Operating-system kernels — Linux links processes, timers and devices with an intrusive circular doubly linked list (
list_head). - Standard libraries — Java's
LinkedListand C++'sstd::listare doubly linked; Python'scollections.dequeis a doubly linked list of fixed-size blocks.
Linked lists in coding interviews
Most interview problems combine four moves, each animated on this site: reverse in place with three pointers; slow and fast pointers to find the middle or detect a cycle (Floyd's tortoise and hare); a dummy head to merge sorted lists or delete without special cases; and gap pointers to remove the k-th node from the end.
Interviewers mostly check the empty list, the one-node list, and the head and tail. Narrate each pointer write in order, exactly as the status line above does.
Time and space complexity
| Case | Time | Space | Why |
|---|---|---|---|
| Access by index | O(n) | O(1) | Follow next i times from the head; there is no address arithmetic. |
| Search by value | O(n) | O(1) | Linear scan; the best case, a match at the head, is O(1). |
| Insert at head | O(1) | O(1) | Two pointer writes: new.next = head, then head = new. |
| Insert at tail | O(1) / O(n) | O(1) | O(1) with a tail pointer; without one you walk to the last node. |
| Insert or delete after a known node | O(1) | O(1) | Only the neighbouring pointers change; nothing is shifted. |
| Delete tail (singly) | O(n) | O(1) | The new tail is the second-to-last node, which only a walk finds. Doubly linked: O(1). |
| Storage | — | O(n) | One node per element, each with one (singly) or two (doubly) pointers. |
Linked List in Python
class Node:
def __init__(self, data):
self.data = data
self.next = None # next node
class LinkedList:
def __init__(self):
self.head = None # first node
def insert_at_head(self, value):
new_node = Node(value)
new_node.next = self.head
self.head = new_node
def insert_at_tail(self, value):
new_node = Node(value)
if self.head is None:
self.head = new_node
return
curr = self.head
while curr.next is not None:
curr = curr.next
curr.next = new_node
def search(self, value):
curr, i = self.head, 0
while curr is not None:
if curr.data == value:
return i
curr, i = curr.next, i + 1
return -1
def delete_head(self):
if self.head is None:
raise IndexError("delete from empty list")
temp = self.head
self.head = self.head.next
del temp # free(temp): the GC reclaims it
def delete_value(self, value):
if self.head is None:
return False
if self.head.data == value:
self.delete_head()
return True
prev = self.head
while prev.next is not None:
if prev.next.data == value:
break
prev = prev.next
if prev.next is None:
return False # not found
temp = prev.next
prev.next = temp.next
del temp # free(temp): the GC reclaims it
return TrueThe visualizer above shows the same code in Python, C++, Java and JavaScript.
Frequently asked questions
Is a linked list faster than an array?
Only for some operations. Inserting or deleting at the front, or next to a node you already hold, is O(1) in a list and O(n) in an array. But indexing is O(n) instead of O(1), and scanning a list is usually slower in practice because its nodes are scattered in memory and miss the CPU cache.
Why is accessing an element of a linked list O(n)?
Nodes are not stored next to each other, so there is no formula for the address of element i. The only way to reach it is to start at the head and follow next i times.
What is the difference between a singly and a doubly linked list?
A singly linked node has one pointer, next, and can only be walked forward. A doubly linked node also has prev, so you can walk backwards and delete any node or the tail in O(1), at the cost of one extra pointer per node.
Does Python have a built-in linked list?
Not as a general list type: Python's list is a dynamic array. collections.deque is implemented as a doubly linked list of fixed-size blocks and gives O(1) appends and pops at both ends; for a textbook linked list you write a small Node class like the one above.
When should I use a linked list instead of an array?
When you insert and delete far more often than you index — especially at the ends or at nodes you already hold — and when elements must keep a stable address while others come and go, as in LRU caches, free lists and kernel data structures.
What is a dummy (sentinel) node in a linked list?
A placeholder node in front of the real head that holds no data. Because every real node then has a predecessor, insert and delete need no special case for index 0, which removes a whole class of edge-case bugs.
Keep learning
- Doubly Linked List VisualizerDoubly linked list visualizer: see prev and next pointers rewired as you insert, delete and reverse, learn why tail delete is O(1), with Python code and FAQs.
- Circular Linked List VisualizerCircular linked list visualizer: the last node points back to the head. See O(1) insert at both ends with a tail pointer, safe traversal and real-world uses.
- Reverse a Linked List — Step by StepReverse a linked list in place with three pointers. Watch every arrow flip step by step and compare iterative vs recursive code — O(n) time, O(1) space.
- Floyd's Cycle Detection VisualizerFloyd's cycle detection (tortoise and hare) finds a loop in a linked list with two pointers and the node where it starts. Step through both phases to see why.
- Stack & Queue VisualizerStack and queue visualizer: animate push, pop, enqueue and dequeue, compare LIFO with FIFO, and step through circular queues, deques and stack applications.
Last updated