Framework Thinking - Neetcode 150 - Remove N-th Node From End of Linked List
Here is solutions for Remove N-th Node From End of Linked List.
1. Understand the problem
- Given the head of a singly linked list and an integer n, remove the n-th node from the end of the list and return the head of the modified list.
2. Clarify constraints (ask 4–5 questions including edge cases)
- What if the list is empty?
- If head is None, just return None.
- What if n is larger than the length of the list?
- Typically constraints guarantee 1 ≤ n ≤ length. If not, add handling.
- What if removing the first node (i.e., n == length)?
- You must return head.next.
- Can the list have only 1 node?
- Yes → if n = 1, return None.
- Are there negative values for n?
- No. n ≥ 1 by constraints.
3. Explore examples
| Linked List | n | Output |
|---|---|---|
1 → 2 → 3 → 4 → 5, n=2 |
1 → 2 → 3 → 5 |
|
1 → 2 → 3 → 4 → 5, n=5 |
2 → 3 → 4 → 5 |
|
1, n=1 |
None |
|
1 → 2, n=1 |
1 |
|
1 → 2, n=2 |
2 |
4. Brainstorm 2-3 solutions
4.1. Solution 1: Naive Solution - Two Pass - Time O(N), Space: O(1).
-
Find length L.
-
Find node at position L - n.
-
Remove next node.
-
Time: O(L).
-
Space: O(1)
4.2. Solution 2: Optimal – Fast & slow pointers - One pass - Gap Fast and Flow is N nodes, fast reach the end, slow in the N-th node from end - Time O(N), Space O(1)
-
Idea: Keep fast above slow N nodes => Keep the gap is N.
-
When fast reach to the end, slow is the nth node before end.
-
Time: O(L).
-
Space: O(1).
5. Implement solutions
5.1. Naive solution - One Pass - Time O(N), Space: O(N).
- Idea: Address store index and access arr[index] = address of the node, clean the node at index len(nodes) - n.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
nodes = []
cur = head
while cur:
nodes.append(cur)
cur = cur.next
removeIndex = len(nodes) - n
if removeIndex == 0:
return head.next
nodes[removeIndex - 1].next = nodes[removeIndex].next
return head
-
Time: O(N).
-
Space: O(N).
5.2. Iteration (Two Pass) - Time O(N), Space: O(1).
- Idea: move pointer but do not use array to store.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
N = 0
cur = head
while cur:
N += 1
cur = cur.next
removeIndex = N - n
if removeIndex == 0:
return head.next
cur = head
for i in range(N - 1):
if (i + 1) == removeIndex:
cur.next = cur.next.next
break
cur = cur.next
return head
-
Time: O(N).
-
Space: O(1).
5.3. Recursion - Time O(N), Space: O(N).
- Idea: Move the pointer address to the end, decrase N when reach the end, if the n[0] = 0, remove the current pointer by point it to head.next (remove this node) => Otherwise keep the head address.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rec(self, head, n):
if not head:
return None
head.next = self.rec(head.next, n)
n[0] -= 1
if n[0] == 0:
return head.next
return head
def removeNthFromEnd(self, head, n):
return self.rec(head, [n])
-
Time: O(N).
-
Space: O(N).
5.4. Fast & slow pointers - One Pass - Time O(N), Space O(1)
- Idea: Keep fast above slow N nodes => Keep the gap is N. When fast reach to the end, slow is the nth node before end.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0, head)
left = dummy
right = head
while n > 0:
right = right.next
n -= 1
while right:
left = left.next
right = right.next
left.next = left.next.next
return dummy.next
-
Time: O(N).
-
Space: O(1).
6. Dry run examples
Example: list = [1 → 2 → 3 → 4 → 5], n = 2
| Step | fast | slow | Notes |
|---|---|---|---|
| init | dummy | dummy | dummy → 1 |
| fast moves 2 steps | 3 | dummy | |
| move both | 4 | 1 | |
| move both | 5 | 2 | |
| move both | None | 3 | stop |
| delete | slow.next=slow.next.next | remove 4 | |
| result | 1 → 2 → 3 → 5 |
November 29, 2025