Given the head
of a linked list, remove the nth
node from the end of the list and return its head.
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:
sz
.1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
Follow up: Could you do this in one pass?
The core challenge of this problem is to remove the nth node from the end of a singly linked list in an efficient manner. This problem is significant in various applications such as managing playlists, task scheduling, and more. A common pitfall is to misunderstand the position of the nth node from the end, especially in edge cases where the list has only one node or when n equals the length of the list.
To solve this problem, we can use a two-pointer technique to achieve the solution in one pass. The idea is to maintain two pointers, with one pointer starting n nodes ahead of the other. When the first pointer reaches the end of the list, the second pointer will be at the node just before the one we need to remove.
A naive solution would involve two passes through the list. In the first pass, we count the total number of nodes. In the second pass, we locate the node to be removed. This approach is not optimal as it requires two passes.
The optimized solution involves using two pointers. We move the first pointer n steps ahead and then move both pointers simultaneously until the first pointer reaches the end. This ensures that the second pointer is at the node just before the one to be removed.
Here is a step-by-step breakdown of the optimized algorithm:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
public class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// Create a dummy node to handle edge cases easily
ListNode dummy = new ListNode(0);
dummy.next = head;
// Initialize two pointers
ListNode first = dummy;
ListNode second = dummy;
// Move the first pointer n steps ahead
for (int i = 0; i <= n; i++) {
first = first.next;
}
// Move both pointers until the first pointer reaches the end
while (first != null) {
first = first.next;
second = second.next;
}
// Remove the nth node from the end
second.next = second.next.next;
// Return the head of the modified list
return dummy.next;
}
}
The time complexity of this approach is O(n), where n is the number of nodes in the list. This is because we traverse the list with the two pointers in a single pass. The space complexity is O(1) as we are using only a constant amount of extra space.
Some potential edge cases include:
These edge cases are handled by the algorithm using a dummy node and careful pointer adjustments.
To test the solution comprehensively, consider the following test cases:
These test cases cover various scenarios, including edge cases and typical cases.
When approaching such problems, consider the following tips:
In this blog post, we discussed how to remove the nth node from the end of a linked list efficiently using a two-pointer technique. We covered the problem definition, approach, algorithm, code implementation, complexity analysis, edge cases, and testing. Understanding and solving such problems is crucial for improving problem-solving skills and preparing for technical interviews.
For further reading and practice, consider the following resources: