Delete a Node from a Linked List
Table of Contents + β
So you built a linked list. Now a student in the list leaves and you need to remove their node. That sounds easy, right? But here is the pain. If you remove a node the wrong way, the chain breaks and the rest of the list is lost. In a linked list every node holds the address of the next node. So deleting a node really means making the node before it point past the deleted one. Get that one link wrong and half your list disappears.
In this tutorial we delete a node in three common ways. We delete the head. We delete the last node. And we delete a node by its value.
π A Quick Picture of the Problem
Think of a linked list like a treasure hunt. Each clue tells you where the next clue is hidden. Now you want to throw away the middle clue. You cannot just tear it up. The clue before it still points to the torn one. So the hunt stops there. You first have to rewrite the earlier clue so it points straight to the clue after the one you removed.
That rewriting is the main idea. The term for it is re-linking, which means changing the next pointer of the previous node so it skips over the node you are deleting.
Here we are deleting 20. Node 10 used to point to 20. After deletion, 10 must point to 30 instead. The node 20 is now cut out of the chain.
Note
In C and C++ you also have to give the memory back with free or delete. If you skip the node but never free it, the memory stays taken but no one can reach it. That is called a memory leak. In Java, Python, and JavaScript the garbage collector cleans it up for you.
π§± Our Node Setup
Every example below uses the same simple node. A node holds a value and a pointer to the next node. The list is 10 -> 20 -> 30 -> 40 and head points to the first node. Each example repeats this node definition so you can run it on its own.
π₯ Case 1: Delete the Head
This is the easy one. The head is the first node. To remove it, you just move head forward to the second node. There is no previous node to worry about. So there is no re-linking.
Here is the catch. Save the old head in a temporary pointer first. If you move head before saving the old one, you lose the address of the node you wanted to free.
Steps to delete the head:
- If the list is empty (
headisNULL), there is nothing to delete. Stop. - Save the current head in a temporary pointer. Call it
temp. - Move
headtohead.next(the second node). - Free
temp(in C and C++).
class Node: def __init__(self, value): self.data = value self.next = None
# Delete the first node and return the new head.def delete_head(head): if head is None: # empty list return None return head.next # second node becomes the new head
# Print every value in the list.def print_list(head): current = head while current is not None: print(current.data, end=" -> ") current = current.next print("NULL")
# Build 10 -> 20 -> 30 -> 40head = Node(10)head.next = Node(20)head.next.next = Node(30)head.next.next.next = Node(40)
print("Before: ", end="")print_list(head)
head = delete_head(head)
print("After: ", end="")print_list(head)The output of the above code will be:
Before: 10 -> 20 -> 30 -> 40 -> NULLAfter: 20 -> 30 -> 40 -> NULLTip
Deleting the head is O(1). You touch only one node and one pointer. It does not matter how long the list is.
π Case 2: Delete the Last Node
The last node is the one whose next is NULL. Deleting it is harder than the head. To cut off the last node, you need the node just before it. That second-to-last node must now point to NULL. Then it becomes the new end.
So you walk the list with two ideas in mind. You move forward until the next node is the last node. Then you stop and re-link.
Steps to delete the last node:
- If the list is empty, stop.
- If the list has only one node, delete it and set
headtoNULL. - Otherwise, walk until you reach the second-to-last node (the one whose
next.nextisNULL). - Free the last node and set the second-to-last nodeβs
nexttoNULL.
class Node: def __init__(self, value): self.data = value self.next = None
# Delete the last node and return the head.def delete_last(head): if head is None: # empty list return None
# Only one node. if head.next is None: return None
# Walk to the second-to-last node. current = head while current.next.next is not None: current = current.next current.next = None # drop the last node return head
def print_list(head): current = head while current is not None: print(current.data, end=" -> ") current = current.next print("NULL")
# Build 10 -> 20 -> 30 -> 40head = Node(10)head.next = Node(20)head.next.next = Node(30)head.next.next.next = Node(40)
print("Before: ", end="")print_list(head)
head = delete_last(head)
print("After: ", end="")print_list(head)The output of the above code will be:
Before: 10 -> 20 -> 30 -> 40 -> NULLAfter: 10 -> 20 -> 30 -> NULLCaution
Deleting the last node is O(n). You have to walk all the way to the end first. There is no shortcut in a singly linked list because a node only knows its next, never its previous one.
π― Case 3: Delete by Value
This is the real-world one. You do not know the position. You only know the value, like βremove the node holding 30β. So you walk the list and look for it.
The trick here is to keep two pointers, which means tracking both the current node and the node right before it. When current lands on the value, you make previous.next point to current.next. That skips the matched node out of the chain.
There is one special case to watch. If the value is in the very first node, there is no previous node. So you just delete the head like in Case 1.
Steps to delete by value:
- If the list is empty, stop.
- If the head holds the value, move
headforward and free the old head. - Otherwise, walk with
previousandcurrentuntilcurrentholds the value. - If found, set
previous.nexttocurrent.next, then freecurrent. - If you reach the end without finding it, the value is not in the list. Do nothing.
class Node: def __init__(self, value): self.data = value self.next = None
# Delete the first node that holds the given value, return the head.def delete_value(head, value): if head is None: # empty list return None
# The value is in the head node. if head.data == value: return head.next
# Walk with previous and current. previous = head current = head.next while current is not None: if current.data == value: previous.next = current.next # re-link past current return head previous = current current = current.next
return head # not found: list unchanged
def print_list(head): current = head while current is not None: print(current.data, end=" -> ") current = current.next print("NULL")
# Build 10 -> 20 -> 30 -> 40head = Node(10)head.next = Node(20)head.next.next = Node(30)head.next.next.next = Node(40)
print("Before: ", end="")print_list(head)
head = delete_value(head, 30) # remove the node holding 30
print("After del 30: ", end="")print_list(head)The output of the above code will be:
Before: 10 -> 20 -> 30 -> 40 -> NULLAfter del 30: 10 -> 20 -> 40 -> NULLCaution
Deleting by value is O(n) in the worst case. The value might be the last node, so you may have to walk the whole list to find it.
β±οΈ Complexity at a Glance
Here is how the three cases compare. Time is how long the work takes as the list grows. Space is the extra memory you use, and for all three it stays tiny.
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Delete the head | O(1) | O(1) |
| Delete the last node | O(n) | O(1) |
| Delete by value | O(n) | O(1) |
β οΈ Common Mistakes to Avoid
These are the mistakes almost everyone makes the first time. Read them slowly.
- Moving
headbefore saving the old head. You lose the address of the node you wanted to free. Always save it intempfirst. - Forgetting to re-link. If you free a node but never fix
previous.next, the previous node still points to freed memory. That is a dangling pointer and your program may crash. - Forgetting to free in C and C++. Skip the node without
freeordeleteand the memory stays taken forever. That is a memory leak. - Not handling the empty list. If
headisNULLand you try to readhead->next, the program crashes. Check for empty first. - Not handling the single-node case when deleting the last node. With one node there is no second-to-last node, so set
headtoNULLdirectly.
π§© What Youβve Learned
β Deleting a node is really about re-linking. The previous node must point past the one you remove.
β
Deleting the head is O(1). You just move head forward to the second node.
β
Deleting the last node is O(n). You walk to the second-to-last node and set its next to NULL.
β
Deleting by value uses two pointers, previous and current, so you can skip the matched node.
β
In C and C++ you must free or delete the removed node. In Java, Python, and JavaScript the garbage collector handles it.
β Always check the empty list and single-node cases before touching pointers.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
When deleting the head of a singly linked list, what must you do before moving the head pointer forward?
Why: If you move head first, you lose the address of the old head and cannot free it.
- 2
Why is deleting the last node of a singly linked list O(n)?
Why: A singly linked node has no pointer to its previous node, so you walk from the head to find the second-to-last node.
- 3
When deleting by value, why do you keep two pointers (previous and current)?
Why: You need the previous node so its next can point past the node you are deleting.
- 4
In C and C++, what happens if you skip a node but never call free or delete on it?
Why: Without free or delete, the removed node's memory is never given back, which is a memory leak.