
Problem Statement
In this problem, we are given the head of a singly linked list. We are tasked to remove the nth node from the end of the list and then return the head of the modified list. The linked list is defined in such a way that each node contains a single value and a reference to the next node.
Examples
Example 1
Input:
head = [1,2,3,4,5], n = 2
Output:
[1,2,3,5]
Example 2
Input:
head = [1], n = 1
Output:
[]
Example 3
Input:
head = [1,2], n = 1
Output:
[1]
Constraints
- The number of nodes in the list is
sz. 1 <= sz <= 300 <= Node.val <= 1001 <= n <= sz
Approach and Intuition
To tackle this problem, we can follow a two-pointer technique often referred to as the "fast and slow" pointer approach. This method is efficient and requires only a single pass through the linked list. Here’s a detailed breakdown:
Initialization:
- Initialize two pointers,
fastandslow. Both are set to start at the head of the linked list.
- Initialize two pointers,
Advance Fast Pointer:
- Move the
fastpointernsteps ahead. This helps in creating a gap ofnnodes between thefastandslowpointers.
- Move the
Move Both Pointers:
- Continue moving both pointers one step at a time. When the
fastpointer reaches the end of the list (fast.nextbecomesnull), theslowpointer will be at the preceding node of the node to be removed.
- Continue moving both pointers one step at a time. When the
Remove nth Node:
- Modify the next pointer of the node where the
slowpointer is located to skip the node directly after it (which is the nth node from the end).
- Modify the next pointer of the node where the
Special Cases to Consider:
- When the list has only one node or
nequals the length of the list:- Directly return
nullor the next of the head depending ifnis1and the size is1, because removing the head is necessary.
- Directly return
- List validation:
- Ensure the linked list is not empty before proceeding with any operations.
This approach ensures that the time complexity is linear, O(n), where n is the number of nodes in the linked list, and it does not require any extra space, except for the two-pointer variables, thus achieving O(1) space complexity.
Solutions
- C++
- Java
- C
- JavaScript
- Python
class Solution {
public:
ListNode* deleteNthFromEnd(ListNode* head, int n) {
ListNode prehead(0);
prehead.next = head;
ListNode* lead = &prehead;
ListNode* follow = &prehead;
// Positioning lead pointer n+1 places from the start
for (int i = 1; i <= n + 1; i++) {
lead = lead->next;
}
// Moving both pointers until lead reaches the end
while (lead != NULL) {
lead = lead->next;
follow = follow->next;
}
// Skipping the nth node from end
follow->next = follow->next->next;
return prehead.next;
}
};
The solution provided details an implementation in C++ to remove the Nth node from the end of a singly linked list. The function deleteNthFromEnd receives the head of the list and the integer n, denoting the Nth position from the end of the list to be removed.
- Initialize a
preheadto an auxiliary node with0as its value and setprehead.nextto point tohead. This helps simplify edge cases like removing the head. - Create two pointers,
leadandfollow, initially set to point to theprehead. - Increment the
leadpointern + 1times. This positions it such that the gap betweenleadandfollowisnnodes. - Traverse the list with both pointers until
leadreaches the end (NULL).leadwill move one node at a time.followwill also move simultaneously withlead, maintaining the gap.
- After this loop,
followpoints to the node immediately before the Nth node from the end. - To remove the Nth node, set
follow.nexttofollow.next.next. This skips the desired node, effectively removing it. - Finally, the function returns the next node of
prehead, which is the potentially new head of the list (if the head was removed).
This solution operates in O(n) time complexity where n is the number of nodes in the list, as it requires a single pass to find and remove the specified node. The space complexity is O(1) since only a fixed number of extra pointers are used.
class Solution {
public ListNode removeNthNodeFromEnd(ListNode startNode, int n) {
ListNode temp = new ListNode(0);
temp.next = startNode;
ListNode front = temp;
ListNode back = temp;
for (int index = 1; index <= n + 1; index++) {
front = front.next;
}
while (front != null) {
front = front.next;
back = back.next;
}
back.next = back.next.next;
return temp.next;
}
}
The provided Java solution addresses the common interview problem of removing the Nth node from the end of a singly linked list. Let's break down the approach:
- Initialize a dummy node (
temp) and point itsnextto the starting node (startNode) of the list. This dummy node helps handle edge cases gracefully, such as removing the head of the list. - Use two pointers,
frontandback, both starting at the dummy node. This maintains a gap ofnnodes between them. - Move the
frontpointern + 1times. This sets up the gap ofnnodes since after movingn + 1times,frontisnnodes ahead ofback. - Traverse the list until
frontis null. For each step offront, movebackone step. This keeps the gap consistent. - After the loop,
back.nextis the node to be removed. Adjust pointers to excludeback.nextfrom the list. - Return the modified list starting from
temp.next, skipping the dummy node.
This method efficiently finds and removes the Nth node with a single pass through the list, achieving O(n) time complexity where n is the number of nodes in the list. No extra space is used, yielding O(1) space complexity (ignoring the input). This solution leverages the two-pointer technique, which is effective in problems requiring elements at specific intervals or ends of a list.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteNthNodeFromEnd(struct ListNode* head, int n) {
struct ListNode placeholder;
placeholder.val = 0;
placeholder.next = head;
struct ListNode* fast = &placeholder;
struct ListNode* slow = &placeholder;
// Pushes fast pointer ahead by n nodes to create the proper distance
for (int i = 1; i <= n + 1; i++) fast = fast->next;
// Traverse until fast reaches the tail, moving both pointers
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
// Skip the nth node from end
slow->next = slow->next->next;
return placeholder.next;
}
The problem involves removing the Nth node from the end of a singly-linked list in the C programming language. The function deleteNthNodeFromEnd accomplishes this by first establishing a placeholder node to handle potential edge cases like removing the head node of the list.
Here's the breakdown of the method used in the provided code:
- Begin by creating a placeholder node (
placeholder) which points to the head of the list. This node acts as a starting point, ensuring the head is easily removable if needed. - Define two pointers,
fastandslow, both initially pointing to this placeholder node. - Move the
fastpointer forward byn+1positions, creating a separation between thefastandslowpointers equal ton. This ensures that whenfastpointer reaches the end of the list,slowpointer is right before the node to be removed. - Traverse through the list until the
fastpointer reaches the end, moving bothfastandslowpointers forward consistently. At each step,fastmoves one node ahead. - Adjust the
nextpointer of the node at theslowpointer to skip the target node (the Nth node from the end), effectively removing it from the list. - Return the
nextnode of the placeholder, which now represents the possibly new head of the list after the removal operation.
This solution is efficient as it only requires a single pass through the list, making it O(n) in terms of time complexity, where n is the number of nodes in the list. The use of a placeholder node simplifies node removal operations, especially at boundary positions like the head of the list.
function deleteNthFromEnd(listHead, nth) {
let prepNode = new ListNode(0);
prepNode.next = listHead;
let lead = prepNode;
let trail = prepNode;
for (let j = 1; j <= nth + 1; j++) {
lead = lead.next;
}
while (lead !== null) {
lead = lead.next;
trail = trail.next;
}
trail.next = trail.next.next;
return prepNode.next;
}
The provided JavaScript function deleteNthFromEnd efficiently removes the N-th node from the end of a singly linked list. The function takes two arguments: listHead, which is the head of the linked list, and nth, which specifies the position from the end of the list of the node to be removed.
Complete the task by following these steps:
- Initialize a new dummy node (
prepNode) and set itsnextpointer to the head of the list (listHead). This dummy node helps handle edge cases such as removing the head node. - Declare two pointer variables,
leadandtrail, and initially point them both to the dummy node. - Move the
leadpointer forward bynth + 1times. This step creates a gap betweenleadandtrailpointers equal tonth + 1, positioning theleadpointer ahead so that when it reaches the end of the list,trailpoints to the node just before the target node. - Proceed with a loop where both
leadandtrailmove one node forward untilleadreaches the end of the list. - When the loop completes,
trail.nextis the node to be removed. Bypass this node by updatingtrail.nexttotrail.next.next, effectively removing the target node from the list. - Finally, return the new head of the list, which is
prepNode.next, as the dummy node should not be part of the returned list.
This approach ensures that the function works in a single pass through the list, making it efficient with a time complexity of O(n) and a spatial complexity of O(1), where n is the number of nodes in the linked list.
class Solution:
def deleteNthFromEnd(self, head_node, n):
"""
:type head_node: ListNode
:type n: int
:rtype: ListNode
"""
buffer_node = ListNode(0)
buffer_node.next = head_node
leading = buffer_node
trailing = buffer_node
# Advances leading pointer by n+1 steps ahead
for i in range(n + 1):
leading = leading.next
# Proceed leading to the end while moving trailing
while leading is not None:
leading = leading.next
trailing = trailing.next
# Skipping the nth node from end
trailing.next = trailing.next.next
return buffer_node.next
This Python function provided, deleteNthFromEnd, removes the nth node from the end of a singly linked list and returns the modified list. Understand the following key points for implementing the function effectively:
- Initially, a dummy node,
buffer_node, is created and points to the head of the list. This node helps in edge cases, such as when the list has only one node or when removing the head node itself. - Both
leadingandtrailingpointers start at the dummy node. - Move the
leadingpointer forward byn + 1positions to create a gap betweenleadingandtrailing. This gap reflects the nth position from the end asleadingmoves. - Traverse through the list until
leadingpoints toNone. During this traverse, bothleadingandtrailingpointers move forward one step at a time, maintaining the gap. - Once
leadingreaches the end of the list, adjust thenextpointer of thetrailingnode to skip the nth node from the end, effectively removing it. - Return the list starting from the next node of the dummy, which accounts for any changes made to the head during the process.
Implement these steps to modify any singly linked list by removing a designated node from its end without having to calculate the list's length.