You are given the beginning of a linked list head, and an integer n.
Remove the nth node from the end of the list and return the beginning of the list.
Example 1:
Input: head = [1,2,3,4], n = 2
Output: [1,2,4]Example 2:
Input: head = [5], n = 1
Output: []Example 3:
Input: head = [1,2], n = 2
Output: [2]Constraints:
sz.1 <= sz <= 300 <= Node.val <= 1001 <= n <= sz
You should aim for a solution with O(N) time and O(1) space, where N is the length of the given list.
A brute force solution would involve storing the nodes of the list into an array, removing the nth node from the array, and then converting the array back into a linked list to return the new head. However, this requires O(N) extra space. Can you think of a better approach to avoid using extra space? Maybe you should first solve with a two pass approach.
We can use a two-pass approach by first finding the length of the list, N. Since removing the nth node from the end is equivalent to removing the (N - n)th node from the front, as they both mean the same. How can you remove the node in a linked list?
For example, consider a three-node list [1, 2, 3]. If we want to remove 2, we update the next pointer of 1 (initially pointing to 2) to point to the node after 2, which is 3. After this operation, the list becomes [1, 3], and we return the head. But, can we think of a more better approach? Maybe a greedy calculation can help.
We can solve this in one pass using a greedy approach. Move the first pointer n steps ahead. Then, start another pointer second at the head and iterate both pointers simultaneously until first reaches null. At this point, the second pointer is just before the node to be removed. We then remove the node that is next to the second pointer. Why does this work?
This greedy approach works because the second pointer is n nodes behind the first pointer. When the first pointer reaches the end, the second pointer is exactly n nodes from the end. This positioning allows us to remove the nth node from the end efficiently.
We store all nodes in an array so we can directly access the node that is n positions from the end.
Once we know which node to delete, we simply adjust the next pointer of the previous node.
len(nodes) - n.0, it means the head must be removed → return head.next.nodes[removeIndex - 1].next to nodes[removeIndex].next.# 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 headWe first count how many nodes are in the list.
Once we know the total length, the node to delete is at position N - n from the start.
We run a second pass to reach the node just before it and skip it.
N.removeIndex = N - n.removeIndex == 0, delete the head → return head.next.removeIndex.next pointer to skip the unwanted node.# 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 headRecursion naturally processes the list from the end toward the start.
When the recursive calls unwind, we count backwards.
When the count reaches the nth node from the end, we skip it by returning head.next instead of the current node.
n each time you return.n becomes 0, this is the node to delete → return its next node.# 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])Use two pointers so that the gap between them is exactly n.
Move the right pointer n steps ahead first.
Then move both pointers together.
When the right pointer reaches the end, the left pointer will be just before the node we must remove.
This avoids counting the entire list and removes the target in one pass.
left at dummyright at headright forward n steps.right reaches the end.left.next is the node to delete → skip it by doing left.next = left.next.next.dummy.next as the updated head.# 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.nextWhen n equals the length of the list, the head node itself must be removed. Without a dummy node or explicit check for this case, the code may crash or return incorrect results. Always verify your solution works when the target is the first node.
The two-pointer technique requires the left pointer to stop at the node before the one to delete. A common mistake is advancing right by n-1 instead of n steps, causing the wrong node to be removed. Carefully trace through a small example to confirm your gap is correct.
After modifying the list, forgetting to return dummy.next (or the updated head) results in returning a stale reference. This is especially problematic when the original head was deleted. Always ensure your return statement reflects any structural changes to the list.