Delete Node in a Linked List: The Copy-and-Skip Trick
Delete Node in a Linked List is LeetCode problem 237. It is one of those problems that seems impossible at first, then feels almost like cheating once you see the trick.
You are given a node in a singly linked list that you want to delete. But here is the catch: you are not given the head of the list. You only have a reference to the node itself. The node is guaranteed not to be the tail.
In a normal linked list deletion, you walk from the head, find the previous node, and rewire its next pointer to skip the target. But you do not have the head. You cannot reach the previous node. So how do you remove something when you cannot change the pointer that leads to it?
The key insight
You cannot actually delete the node from memory. What you can do is make it look like it was never there.
Instead of removing the node, you overwrite it. Copy the value from the next node into the current node, then delete the next node by skipping over it. The effect is identical: the value you wanted gone is gone, and the list is one node shorter.
Think of it like this. You want to remove person 5 from a line of people, but you can only talk to person 5. So you tell person 5 to pretend to be person 1 (the one behind them), and then person 1 quietly leaves. From the front of the line, nobody can tell the difference.
This trick only works because the node is guaranteed not to be the tail. If it were the tail, there would be no next node to copy from, and you could not set a previous pointer to null without access to that previous node.
The solution
def delete_node(node):
node.val = node.next.val
node.next = node.next.next
That is it. Two lines. Copy the next value, then skip over the next node.
Line one overwrites the current node's value with its neighbor's value. Line two rewires the current node's next pointer to skip over that neighbor entirely. The neighbor becomes unreachable and gets garbage collected.
Step-by-step walkthrough
Let's trace through deleting node 5 from the list 4 -> 5 -> 1 -> 9. We only have a reference to the node containing 5.
Step 1: Initial list. We are given the node with value 5.
We only have a reference to the node containing 5. No access to the head or the previous node.
Step 2: Copy the next node's value (1) into the current node.
node.val = node.next.val. The current node now holds value 1. The old next node is now redundant.
Step 3: Point node.next to node.next.next, skipping the old next node.
node.next = node.next.next. The duplicate node (crossed out) is now unreachable.
Step 4: Done. The list is now 4 -> 1 -> 9.
The node with value 5 has been effectively removed. Two operations, constant time.
Notice how we never touched node 4 at all. We did not need to. From node 4's perspective, the node in front of it now holds value 1 instead of 5. The list is shorter by one node, and the value 5 is gone. Mission accomplished.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Copy and skip | O(1) | O(1) |
Both operations are constant time. You copy one value and update one pointer. No traversal, no extra memory. This is about as efficient as it gets.
Building blocks
This problem is built from one core idea:
Value overwriting as a stand-in for structural deletion. When you cannot change the pointers leading into a node, you can still change the node's contents. By copying data forward and deleting the next node instead, you achieve the same result from the caller's perspective.
This pattern shows up less often than standard linked list deletion, but the underlying principle of working around access limitations is valuable. Many problems require creative thinking about what you can modify versus what you cannot.
The more direct pattern here is the pointer skip: setting node.next = node.next.next. This is the same operation used in any linked list removal. The only difference is which node you are skipping. Normally you skip the target. Here you skip the target's neighbor after cloning its data.
Edge cases
- Node is second-to-last. The node's next is the tail. After copying,
node.next.nextisnull, so the current node becomes the new tail. This works correctly. - List has exactly two nodes. You are given the first node. After the operation, the list has one node with the value of the old second node. Correct.
- All nodes have the same value. The copy operation writes the same value that was already there. The list still shrinks by one node. Correct, even if the values are indistinguishable.
- Node is the tail. This violates the problem's constraints. The trick does not work because there is no next node to copy from. The problem guarantees this will not happen.
In real-world code, deleting a node without head access is rare. Most linked list APIs give you the head or an iterator. This problem is more of a puzzle about pointer mechanics than a practical pattern. But it tests whether you understand what "delete" really means in a linked list context.
From understanding to recall
This is a two-line solution. You will read it, understand it instantly, and assume you will remember it. But two weeks from now, under interview pressure, you might hesitate. "Do I copy the value first or skip the pointer first?" "Wait, am I deleting the current node or the next one?"
The trick is not complicated, but it is easy to second-guess. Spaced repetition turns that moment of hesitation into instant recall. You practice typing the solution a few times over increasing intervals, and it becomes automatic. When you see "delete a node without head access," your hands just write the two lines.
Related posts
- Reverse Linked List - The most fundamental linked list pointer manipulation problem
- Remove Nth Node From End of List - A different deletion challenge where you need to find the right node first
- Linked List Cycle - Another problem that tests your understanding of how linked list pointers work