Generated by Codex with GPT-5
Quick facts
- Difficulty:
MEDIUM - Problem: Delete Node in a Linked List
- Main tags:
Linked List
What the problem is really asking
This problem sounds impossible the first time it is heard.
In a normal singly linked list, deleting a node means changing the previous node’s next pointer so it skips the node being removed. But this problem does not give the head of the list or the previous node. It gives only the node that should disappear, and it guarantees that this node is not the tail.
So the real question is not “how do I unlink this exact node object?” The real question is:
how do I make the list look exactly as if this node were deleted, even though the predecessor is unavailable?
That shift in perspective is the whole problem.
The key insight
Since the node to delete is guaranteed not to be the tail, it always has a successor.
That means the current node can steal the successor’s contents:
- Copy the successor’s value into the current node.
- Point the current node at the successor’s next node.
After that, the successor is the node that has been unlinked from the chain.
For example, if the list segment is:
5 -> 1 -> 9
and the node containing 5 is “deleted,” the algorithm does this:
- Copy
1into the5node. - Change its
nextpointer to skip the original1node.
Now the visible list segment becomes:
1 -> 9
So the list has the correct final shape, even though the original node object was not literally removed from memory.
How to derive the optimal solution
The clean way to derive it is to start from what true deletion usually requires.
In a singly linked list, deleting node X normally means:
previous.next = X.next
That immediately reveals the problem: there is no access to previous, so the standard deletion operation cannot be performed directly.
At that point, there are only two useful facts left:
- the current node can still be modified
- the current node definitely has a next node
If the predecessor cannot be changed, then the only workable option is to change the current node so it behaves like its successor. Once the current node copies the successor’s value and pointer, the successor becomes redundant and can be bypassed.
That gives the optimal solution immediately:
- Time:
O(1) - Extra space:
O(1)
No traversal is needed because the answer uses only the given node and its next node.
Why this works
The only subtle point is that the algorithm does not physically remove the exact node object passed in.
Instead, it removes the next node and overwrites the current node so the list contents match the expected result. For this LeetCode problem, that is exactly what correctness means. The judge checks the resulting list values and structure, not object identity.
This is also why the “not the tail” constraint matters so much. Without a successor to copy from, there is no way to finish the transformation.
Python solution
The implementation below keeps the mutation logic in a helper function and leaves the LeetCode wrapper thin. It also adds validation that would be useful in production code, even though LeetCode guarantees the input is valid.
# LeetCode provides the ListNode class.
def delete_node_in_place(node: "ListNode") -> None:
"""
Delete a node from a singly linked list when only that node is given.
This works by copying the successor into the current node and then
bypassing the successor.
"""
successor = _require_successor(node)
# Overwrite the current node so it takes on the successor's identity
# from the list's point of view.
node.val = successor.val
node.next = successor.next
# Break the detached node's link to make the mutation clearer in
# non-LeetCode settings and to avoid keeping an unnecessary reference.
successor.next = None
def _require_successor(node: "ListNode") -> "ListNode":
if node is None:
raise ValueError("node must not be None")
if node.next is None:
raise ValueError("cannot delete the tail node with this API")
return node.next
class Solution:
def deleteNode(self, node: "ListNode") -> None:
"""LeetCode entry point."""
delete_node_in_place(node)Interview follow-ups
What if the node to delete might be the tail?
Then this exact trick stops working. The method depends on copying data from the next node, and a tail node has no successor. In a singly linked list, truly deleting the tail requires access to its predecessor so that predecessor can be rewired to None. Without either the head of the list or a direct predecessor reference, deleting an arbitrary tail node is impossible in general. The complexity only stays O(1) when the right structural information is available.
Does this really delete the given node?
Not in the strict object-identity sense. The node object passed in remains in the list, but its value and pointer are overwritten so it becomes indistinguishable from its original successor when looking only at list contents. The actual node that gets unlinked is the successor. That distinction does not matter for LeetCode, because the observable behavior is the final list shape. In a real system where other code may hold references to specific node objects, though, that semantic difference can matter a lot.
What if node values are immutable or too expensive to copy?
This solution depends on mutating the current node’s payload. If node values are immutable, or if each node contains a large object that should not be copied, then the trick may no longer be acceptable. In that setting, the system usually needs a different API, such as access to the head of the list, a predecessor pointer, or a doubly linked list structure. The asymptotic tradeoff is straightforward: the current problem achieves O(1) time only because it allows direct in-place mutation of the node contents.
How would this change in a doubly linked list?
A doubly linked list makes true deletion much simpler because each node can reach both neighbors. If the current node has prev and next pointers, then deletion can usually be done by reconnecting those two neighbors directly, without copying any values. That still runs in O(1) time and O(1) extra space, but it is conceptually cleaner because the actual target node can be removed rather than replaced in place. The main extra work is handling boundary cases such as deleting the head or tail.
What if the interviewer changes the question and gives the head instead?
Then the problem becomes an ordinary linked-list deletion problem rather than a trick question. The algorithm would traverse from the head until it finds the predecessor of the target node, then update that predecessor’s next pointer to skip the target. That takes O(n) time in the worst case and O(1) extra space. Compared with LeetCode 237, the runtime is worse, but the semantics are stronger because the exact node can be removed normally.