Remove Linked List Elements in O(n) Time Complexity using JavaScript


Given the head of a singly linked list and an integer val, remove all the nodes of the linked list that has Node.val == val, and return the new head.

Example:

Input: head = [1, 2, 6, 3, 4, 5, 6], val = 6
Output: [1, 2, 3, 4, 5]


Note:

Your algorithm should run in O(n) time and use O(1) extra space.


Understanding the Problem

The core challenge of this problem is to traverse the linked list and remove nodes that match the given value without using extra space. This is a common problem in linked list manipulation and has applications in data cleaning and filtering operations.

Potential pitfalls include handling the removal of nodes at the beginning of the list and ensuring that the list remains connected after nodes are removed.

Approach

To solve this problem, we can use a two-pointer technique. One pointer will traverse the list, and the other will keep track of the previous node to help with node removal.

1. **Naive Solution**: Traverse the list and use an array to store nodes that do not match the value. This approach is not optimal as it uses extra space.

2. **Optimized Solution**: Use a dummy node to handle edge cases where the head needs to be removed. Traverse the list with a single pointer and adjust the next pointers to remove nodes.

Algorithm

1. Create a dummy node and set its next pointer to the head of the list.

2. Initialize a pointer to the dummy node.

3. Traverse the list. For each node, check if it matches the given value.

4. If it matches, adjust the next pointer of the previous node to skip the current node.

5. If it does not match, move the previous pointer to the current node.

6. Continue until the end of the list.

7. Return the next pointer of the dummy node as the new head of the list.

Code Implementation

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

function removeElements(head, val) {
    // Create a dummy node to handle edge cases
    let dummy = new ListNode(0);
    dummy.next = head;
    
    // Initialize current node to dummy
    let current = dummy;
    
    // Traverse the list
    while (current.next !== null) {
        if (current.next.val === val) {
            // Skip the node with the matching value
            current.next = current.next.next;
        } else {
            // Move to the next node
            current = current.next;
        }
    }
    
    // Return the new head
    return dummy.next;
}

// Example usage:
let head = new ListNode(1, new ListNode(2, new ListNode(6, new ListNode(3, new ListNode(4, new ListNode(5, new ListNode(6)))))));
let val = 6;
let newHead = removeElements(head, val);
console.log(newHead); // Output: [1, 2, 3, 4, 5]

Complexity Analysis

The time complexity of this solution is O(n) because we traverse the list once. The space complexity is O(1) as we only use a few extra pointers.

Edge Cases

1. The list is empty. The function should return null.

2. All nodes have the value to be removed. The function should return null.

3. The head node has the value to be removed. The function should correctly update the head.

Testing

To test the solution, we can use various test cases:

We can use console logs or a testing framework like Jest to verify the outputs.

Thinking and Problem-Solving Tips

1. Break down the problem into smaller parts and solve each part step by step.

2. Use diagrams to visualize the linked list and the changes made during traversal.

3. Practice similar problems to improve your understanding of linked list manipulation.

Conclusion

In this blog post, we discussed how to remove elements from a linked list in O(n) time complexity using JavaScript. 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 problem-solving skills and preparing for technical interviews.

Additional Resources