Delete a Node from a Linked List

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.

new link

10

20

30

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:

  1. If the list is empty (head is NULL), there is nothing to delete. Stop.
  2. Save the current head in a temporary pointer. Call it temp.
  3. Move head to head.next (the second node).
  4. Free temp (in C and C++).
delete_head.py
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 -> 40
head = 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 -> NULL
After: 20 -> 30 -> 40 -> NULL

Tip

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:

  1. If the list is empty, stop.
  2. If the list has only one node, delete it and set head to NULL.
  3. Otherwise, walk until you reach the second-to-last node (the one whose next.next is NULL).
  4. Free the last node and set the second-to-last node’s next to NULL.
delete_last.py
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 -> 40
head = 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 -> NULL
After: 10 -> 20 -> 30 -> NULL

Caution

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:

  1. If the list is empty, stop.
  2. If the head holds the value, move head forward and free the old head.
  3. Otherwise, walk with previous and current until current holds the value.
  4. If found, set previous.next to current.next, then free current.
  5. If you reach the end without finding it, the value is not in the list. Do nothing.
delete_value.py
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 -> 40
head = 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 -> NULL
After del 30: 10 -> 20 -> 40 -> NULL

Caution

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 head before saving the old head. You lose the address of the node you wanted to free. Always save it in temp first.
  • 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 free or delete and the memory stays taken forever. That is a memory leak.
  • Not handling the empty list. If head is NULL and you try to read head->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 head to NULL directly.

🧩 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. 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. 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. 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. 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.

πŸš€ What’s Next?

Share & Connect