A clear explanation of removing all linked list nodes with a target value using iteration and a dummy node.
Problem Restatement
We are given the head of a singly linked list and an integer val.
We need to remove every node whose value equals val.
Return the new head of the linked list after all removals.
The official statement says: given the head of a linked list and an integer val, remove all the nodes of the linked list that have Node.val == val, and return the new head.
Input and Output
| Item | Meaning |
|---|---|
| Input | Head of a singly linked list and integer val |
| Output | Head of the modified linked list |
| Goal | Remove every node whose value equals val |
| Important case | The head itself may need to be removed |
Typical node definition:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = nextExample function shape:
def removeElements(head: ListNode, val: int) -> ListNode:
...Examples
Example 1:
head = [1,2,6,3,4,5,6]
val = 6Remove every 6.
Result:
[1,2,3,4,5]Example 2:
head = []
val = 1The list is already empty.
Result:
[]Example 3:
head = [7,7,7,7]
val = 7Every node must be removed.
Result:
[]Understanding Linked List Removal
Suppose we have:
1 -> 2 -> 6 -> 3We want to remove 6.
The node before 6 is 2.
Instead of pointing to 6, node 2 should point directly to 3.
Before:
2 -> 6 -> 3After:
2 -> 3So linked list deletion works by changing pointers.
First Thought
We can walk through the list node by node.
For each node:
- If the next node should be removed, skip it.
- Otherwise, move forward.
The main difficulty is removing the head node.
For example:
6 -> 1 -> 2If we remove the first node, the head changes.
Handling this separately creates extra edge cases.
Key Insight: Use a Dummy Node
A dummy node is a fake node placed before the real head.
Example:
dummy -> 1 -> 2 -> 6 -> 3Now every real node has a previous node, even the original head.
This makes removal logic uniform.
If the head must be removed, we simply update:
dummy.nextinstead of handling a special case.
Algorithm
- Create a dummy node.
- Point the dummy node to the original head.
- Use a pointer called
current. - While
current.nextexists:- If
current.next.val == val, skip that node. - Otherwise, move
currentforward.
- If
- Return
dummy.next.
Walkthrough
Use:
1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6
val = 6Initial structure:
dummy -> 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6Start with:
current = dummyMove through the list.
When current points to node 2:
2 -> 6 -> 3The next node has value 6, so skip it:
current.next = current.next.nextNow:
2 -> 3Continue scanning.
Near the end:
5 -> 6Skip the final 6.
Final list:
1 -> 2 -> 3 -> 4 -> 5Return:
dummy.nextCorrectness
The algorithm maintains the invariant that all nodes before current are already correctly processed.
At each step, the algorithm examines current.next.
If current.next.val == val, that node must be removed. The assignment:
current.next = current.next.nextremoves the node from the linked list by bypassing it.
No valid nodes are lost, because the remaining part of the list stays connected.
If current.next.val != val, the node should remain in the list, so the algorithm advances:
current = current.nextThe loop continues until every node has been examined.
The dummy node guarantees that even if the original head must be removed, the list remains correctly connected and accessible through:
dummy.nextTherefore, every node with value val is removed, and every other node remains in the final list.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Each node is visited at most once |
| Space | O(1) | Only a few pointers are used |
Implementation
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
dummy = ListNode(0)
dummy.next = head
current = dummy
while current.next:
if current.next.val == val:
current.next = current.next.next
else:
current = current.next
return dummy.nextCode Explanation
Create a dummy node:
dummy = ListNode(0)
dummy.next = headNow every node has a previous node.
Start scanning from the dummy node:
current = dummyContinue while there is a next node:
while current.next:If the next node should be removed:
if current.next.val == val:skip it:
current.next = current.next.nextOtherwise move forward:
current = current.nextFinally return the real head:
return dummy.nextRecursive Solution
We can also solve the problem recursively.
Process the rest of the list first, then decide whether to keep the current node.
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
if not head:
return None
head.next = self.removeElements(head.next, val)
if head.val == val:
return head.next
return headIdea:
- Recursively clean the remaining list.
- If the current node should be removed, return the cleaned remainder.
- Otherwise return the current node.
The iterative dummy-node solution is usually preferred in interviews because it avoids recursion depth concerns.
Testing
def build_list(values):
dummy = ListNode(0)
current = dummy
for value in values:
current.next = ListNode(value)
current = current.next
return dummy.next
def to_list(head):
result = []
while head:
result.append(head.val)
head = head.next
return result
def run_tests():
s = Solution()
head = build_list([1,2,6,3,4,5,6])
assert to_list(s.removeElements(head, 6)) == [1,2,3,4,5]
head = build_list([])
assert to_list(s.removeElements(head, 1)) == []
head = build_list([7,7,7,7])
assert to_list(s.removeElements(head, 7)) == []
head = build_list([1,2,3])
assert to_list(s.removeElements(head, 4)) == [1,2,3]
head = build_list([6,1,2])
assert to_list(s.removeElements(head, 6)) == [1,2]
print("all tests passed")
run_tests()| Test | Why |
|---|---|
[1,2,6,3,4,5,6] | Standard mixed removals |
[] | Empty list |
[7,7,7,7] | Remove every node |
[1,2,3] with 4 | No removals |
[6,1,2] | Head removal case |