Remove Nth Node from End of List - JavaScript Solution with O(n) Time Complexity


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:

  • The number of nodes in the list is sz.
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

 

Follow up: Could you do this in one pass?

Understanding the Problem

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 many real-world applications, such as managing playlists, task lists, or any other sequential data structure where elements need to be dynamically removed.

Potential pitfalls include off-by-one errors and handling edge cases such as removing the first or last node in the list.

Approach

To solve this problem, we can use a two-pointer technique to achieve a one-pass solution. The idea is to maintain two pointers that are n nodes apart. When the second pointer reaches the end of the list, the first pointer will be at the node just before the one we need to remove.

Here is a step-by-step approach:

  1. Initialize two pointers, both starting at the head of the list.
  2. Move the second pointer n steps ahead.
  3. Move both pointers one step at a time until the second pointer reaches the end of the list.
  4. The first pointer will now be at the node just before the one to be removed. Adjust the pointers to remove the target node.

Algorithm

Let's break down the algorithm step-by-step:

  1. Create a dummy node that points to the head of the list. This helps in handling edge cases where the head needs to be removed.
  2. Initialize two pointers, `first` and `second`, both pointing to the dummy node.
  3. Move the `second` pointer n+1 steps ahead to maintain a gap of n nodes between `first` and `second`.
  4. Move both pointers one step at a time until `second` reaches the end of the list.
  5. Adjust the `next` pointer of the `first` pointer to skip the target node.
  6. Return the head of the modified list.

Code Implementation

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */

/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    // Create a dummy node that points to the head
    let dummy = new ListNode(0);
    dummy.next = head;
    
    // Initialize two pointers, both starting at the dummy node
    let first = dummy;
    let second = dummy;
    
    // Move the second pointer n+1 steps ahead
    for (let i = 0; i <= n; i++) {
        second = second.next;
    }
    
    // Move both pointers until the second pointer reaches the end
    while (second !== null) {
        first = first.next;
        second = second.next;
    }
    
    // Adjust the next pointer of the first pointer to skip the target node
    first.next = first.next.next;
    
    // Return the head of the modified list
    return dummy.next;
};

Complexity Analysis

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 second pointer and then again with both pointers. The space complexity is O(1) as we are using only a constant amount of extra space.

Edge Cases

Some potential edge cases include:

Our algorithm handles these cases by using a dummy node and adjusting pointers accordingly.

Testing

To test the solution comprehensively, consider the following test cases:

head = [1,2,3,4,5], n = 2  => [1,2,3,5]
head = [1], n = 1          => []
head = [1,2], n = 1        => [1]
head = [1,2], n = 2        => [2]

Use a testing framework like Jest or Mocha to automate these tests and ensure the correctness of the solution.

Thinking and Problem-Solving Tips

When approaching such problems, consider the following tips:

Conclusion

In this blog post, we discussed how to remove the nth node from the end of a linked list 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 your algorithmic thinking and coding skills.

Additional Resources

For further reading and practice, consider the following resources: