Python doesn't have a built-in linked list. That's actually a good thing, because building one yourself is the fastest way to understand how pointers work, how memory allocation differs from arrays, and why interviewers love asking about them.
We're going to build a singly linked list from scratch. No hand-waving. Every method explained, every edge case covered. And there's an interactive visualizer halfway through so you can actually see what the code does.
What Even Is a Linked List
A linked list is a chain of nodes. Each node holds two things: a value, and a reference to the next node. The last node points to None. That's it.
Unlike a Python list (which is really an array under the hood), a linked list doesn't store elements next to each other in memory. Each node can live anywhere. They're connected only by the .next pointers.
This means inserting at the head is O(1), you just create a node and point it to the old head. But accessing the 5th element is O(n), you have to walk through nodes 1, 2, 3, 4 to get there.
The Node Class
Every linked list starts here. A node is dead simple:
1class Node:2 def __init__(self, data):3 self.data = data4 self.next = None # pointer to next node
That's the entire Node class. self.data holds the value. self.next holds a reference to the next node (or None if it's the last one). Two attributes.
The LinkedList Class
The linked list itself just needs to keep track of one thing: the head node. Everything else is built from there by walking the chain.
1class LinkedList:2 def __init__(self):3 self.head = None # empty list45 def is_empty(self):6 return self.head is None
An empty linked list is just self.head = None. No pre-allocated memory. No capacity. It grows one node at a time.
Now let's actually add some data to this thing. But first, play with the visualizer below. It'll make the next sections way easier to follow.
Try It Live
Append a few values. Then try prepend, delete, and reverse. Watch how the pointers change. This is exactly what your Python code is doing under the hood.
Append values to build a linked list
List contents
Empty
Append a value or build a random linked list to get started.
Append (Add to End)
Appending means adding a new node at the end of the list. You have to walk to the last node first, because we don't have a tail pointer.
1def append(self, data):2 new_node = Node(data)34 if self.head is None: # empty list?5 self.head = new_node # new node becomes head6 return78 current = self.head # start at head9 while current.next: # walk to the end10 current = current.next11 current.next = new_node # link last node to new node
The key thing here: we have to check if the list is empty first. If self.head is None, the new node just becomes the head. Otherwise, we walk to the end with a while loop and attach the new node there. O(n) because of the walk.
Prepend (Add to Front)
Adding to the front is O(1) this is linked list strong suit.
1def prepend(self, data):2 new_node = Node(data)3 new_node.next = self.head # point to old head4 self.head = new_node # new node is now head
Two lines of actual logic. Create the node, point it to the current head, make it the new head. Done. This works even if the list is empty, because new_node.next would just be None, which is correct.
Delete by Value
Deletion is where people mess up in interviews. You need to handle three cases: deleting the head, deleting a middle node, and the value not existing.
1def delete(self, data):2 if self.head is None: # empty list3 return45 if self.head.data == data: # deleting the head?6 self.head = self.head.next # skip it7 return89 current = self.head10 while current.next: # walk the list11 if current.next.data == data:12 current.next = current.next.next # skip the node13 return14 current = current.next
The trick is looking one node ahead: current.next.data == data. When you find a match, you skip over that node by pointing current.next to current.next.next. The deleted node just becomes garbage collected.
Search
Walk the list. Compare each node. Return True or False. About as straightforward as it gets.
1def search(self, data):2 current = self.head3 while current:4 if current.data == data:5 return True6 current = current.next7 return False
O(n). No way around it. If the value is at the end, you walk the entire list. If it doesn't exist, you also walk the entire list. That's the tradeoff.
Reverse (The Interview Classic)
This is the most-asked linked list interview question. Three pointers: prev, curr, next_node. You flip each arrow one node at a time.
1def reverse(self):2 prev = None3 curr = self.head45 while curr: # until we run off the end6 next_node = curr.next # save the next node7 curr.next = prev # flip the arrow8 prev = curr # advance prev9 curr = next_node # advance curr1011 self.head = prev # prev is the new head
Go back to the visualizer above and hit “Reverse.” Watch the arrows flip one by one. That's exactly what this code does. prev starts as None, curr starts at head, and each iteration flips one pointer. When curr hits None, prev is pointing at what used to be the last node, which is now the first. O(n) time, O(1) space.
The Full Code
Here's everything together. Copy it, run it, break it, fix it. That's how you learn.
1class Node:2 def __init__(self, data):3 self.data = data4 self.next = None567class LinkedList:8 def __init__(self):9 self.head = None1011 def append(self, data):12 new_node = Node(data)13 if not self.head:14 self.head = new_node15 return16 curr = self.head17 while curr.next:18 curr = curr.next19 curr.next = new_node2021 def prepend(self, data):22 new_node = Node(data)23 new_node.next = self.head24 self.head = new_node2526 def delete(self, data):27 if not self.head:28 return29 if self.head.data == data:30 self.head = self.head.next31 return32 curr = self.head33 while curr.next:34 if curr.next.data == data:35 curr.next = curr.next.next36 return37 curr = curr.next3839 def search(self, data):40 curr = self.head41 while curr:42 if curr.data == data:43 return True44 curr = curr.next45 return False4647 def reverse(self):48 prev = None49 curr = self.head50 while curr:51 next_node = curr.next52 curr.next = prev53 prev = curr54 curr = next_node55 self.head = prev5657 def display(self):58 nodes = []59 curr = self.head60 while curr:61 nodes.append(str(curr.data))62 curr = curr.next63 print(" -> ".join(nodes) + " -> None")
Linked List vs Python List
“Why would I ever use this when Python has lists?” Honest answer: in Python specifically, you probably wouldn't in production code. But understanding the difference is critical for interviews.
| Operation | Linked List | Python list | Winner |
|---|---|---|---|
| Insert at front | O(1) | O(n) | Linked List |
| Insert at end | O(n)* | O(1) amortized | Python list |
| Access by index | O(n) | O(1) | Python list |
| Delete at front | O(1) | O(n) | Linked List |
| Search | O(n) | O(n) | Tie |
| Memory per element | More (data + pointer) | Less (contiguous) | Python list |
*O(1) if you keep a tail pointer. We didn't in our implementation to keep it simple.
Linked List Interview Problems
You've built the data structure. Here are the problems interviewers actually ask about it:
Reverse a linked list
You just built this. The three-pointer approach. They want you to write it from memory.
Detect a cycle
Floyd's tortoise and hare. Two pointers, one moves 2x speed. If they meet, there's a cycle.
Find the middle node
Fast pointer moves 2 steps, slow moves 1. When fast hits the end, slow is at the middle.
Merge two sorted lists
Compare heads, pick the smaller one, advance that pointer. Repeat until both are empty.
Remove nth from end
Two pointers, n nodes apart. When the leader hits the end, the follower is at the target.
Check if palindrome
Find middle, reverse the second half, compare node by node.
Can you code a linked list reversal while explaining your approach?
Reading this tutorial is step one. Writing the code under time pressure while an AI interviewer asks you about edge cases is step two.
Try a free mock interviewarrow_forwardFrequently Asked Questions
Does Python have a built-in linked list?add
No. Python's built-in list is a dynamic array, not a linked list. If you need a linked list in Python, you build one yourself with classes. collections.deque is implemented as a doubly linked list under the hood, but it doesn't expose the node structure.
When should I use a linked list instead of a Python list?add
In practice, almost never. Python's list is faster for almost everything due to cache locality. Linked lists matter in interviews and in specific systems programming scenarios where you need O(1) insertion/deletion at the head without shifting elements. In Python, use deque if you need fast append/pop from both ends.
What is the time complexity of linked list operations?add
Prepend is O(1). Append is O(n) without a tail pointer, O(1) with one. Search is O(n). Delete is O(n) because you need to find the node first. Reverse is O(n) time and O(1) space.
How do I reverse a linked list in Python?add
Use three variables: prev (starts as None), curr (starts as head), and next_node. At each step, save curr.next, point curr.next to prev, then advance prev and curr. When curr is None, prev is the new head. This runs in O(n) time with O(1) extra space.
What linked list problems show up in coding interviews?add
The most common are: reverse a linked list, detect a cycle (Floyd's algorithm), find the middle node (fast/slow pointers), merge two sorted lists, remove the nth node from the end, and check if a linked list is a palindrome.
