Given the head
of a linked list, remove the n
th node from the end of the list and return its head.
For example:
Input: head = [1,2,3,4,5], n = 2 Output: [1,2,3,5]
In this case, the second to last element 4
would be removed, and the head of the new list would be [1,2,3,5]
Input: head = [1], n = 1 Output: []
In this case, the first and only element would be removed, and the head of the new list would be an empty list, so null
would be returned.
Input: head = [1,2], n = 1 Output: [1]
In this case, the last element would be removed, and the head of the new list would be [1]
Write a function that takes in the head of a linked list and an integer n
, and removes the n
th node from the end of the list and returns the head.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach finds the node to remove by first figuring out the total number of nodes in the list. Then, it goes through the list again to find the node that's a certain distance from the beginning, corresponding to the node we want to remove from the end.
Here's how the algorithm would work step-by-step:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def remove_nth_from_end_brute_force(head: ListNode, n: int) -> ListNode:
list_length = 0
current_node = head
# Calculate length of the linked list
while current_node:
list_length += 1
current_node = current_node.next
node_to_remove_index = list_length - n
# Handle case where the head node needs to be removed
if node_to_remove_index == 0:
return head.next
current_node = head
# Traverse to node before the node to be removed
for _ in range(node_to_remove_index - 1):
current_node = current_node.next
# Remove the nth node from the end
current_node.next = current_node.next.next
return head
The trick is to use two pointers that are a specific distance apart to find the node to remove in one pass. By keeping a fixed gap between them, we can identify the node right before the one we want to remove without knowing the list's length beforehand.
Here's how the algorithm would work step-by-step:
class ListNode:
def __init__(self, value=0, next_node=None):
self.value = value
self.next_node = next_node
def remove_nth_from_end(head, n):
leading_pointer = head
trailing_pointer = head
# Advance the leading pointer by n steps
for _ in range(n):
if not leading_pointer:
return head
leading_pointer = leading_pointer.next_node
# If leading pointer reaches the end first, remove the head
if not leading_pointer:
return head.next_node
# Advance both pointers until leading pointer reaches the end.
while leading_pointer.next_node:
leading_pointer = leading_pointer.next_node
trailing_pointer = trailing_pointer.next_node
# trailing pointer is now one node before the one to remove
trailing_pointer.next_node = trailing_pointer.next_node.next_node
return head
Case | How to Handle |
---|---|
Empty list (head is null) | Return null immediately, as there are no nodes to remove. |
n is greater than the length of the list | Return the original list without modification, or throw an exception if specified by the prompt. |
List has only one node and n is 1 | Return null, as removing the only node results in an empty list. |
n is equal to the length of the list (removing the head) | Return head.next, effectively making the second node the new head. |
List has two nodes and n is 1 | Remove the last node and return the head. |
List has two nodes and n is 2 | Remove the first node (head) and return the second node. |
Very large list to ensure algorithm efficiency. | Two-pointer approach maintains O(1) space complexity regardless of list size, and O(N) time. |
Integer overflow when calculating list length or positions. | Using appropriate data types (e.g., long) or carefully designed loops avoids overflows. |