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


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 disrupting the structure of the list. This problem is significant in various applications such as data cleaning, where unwanted elements need to be removed efficiently.

Potential pitfalls include handling edge cases such as an empty list, all nodes having the target value, or the target value not being present in the list.

Approach

To solve this problem, we can use a single pass through the linked list, which ensures an O(n) time complexity. We will maintain a pointer to the current node and a pointer to the previous node to help with node removal.

Here is a step-by-step approach:

  1. Initialize a dummy node that points to the head of the list. This helps handle edge cases where the head itself needs to be removed.
  2. Use two pointers: one for the current node and one for the previous node.
  3. Traverse the list. If the current node's value matches the target value, adjust the previous node's next pointer to skip the current node.
  4. Otherwise, move the previous pointer to the current node.
  5. Move the current pointer to the next node.
  6. Return the next node of the dummy node as the new head of the list.

Algorithm

Here is a detailed breakdown of the algorithm:

  1. Create a dummy node and set its next pointer to the head of the list.
  2. Initialize two pointers: prev (pointing to the dummy node) and curr (pointing to the head).
  3. While curr is not null:
    • If curr's value equals the target value, set prev's next to curr's next.
    • Otherwise, move prev to curr.
    • Move curr to the next node.
  4. Return dummy's next as the new head of the list.

Code Implementation


#include <iostream>
using namespace std;

// Definition for singly-linked list.
struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // Create a dummy node that points to the head
        ListNode* dummy = new ListNode(0, head);
        ListNode* prev = dummy;
        ListNode* curr = head;
        
        // Traverse the list
        while (curr != nullptr) {
            if (curr->val == val) {
                // Remove the current node
                prev->next = curr->next;
            } else {
                // Move prev to curr
                prev = curr;
            }
            // Move curr to the next node
            curr = curr->next;
        }
        
        // Return the new head
        ListNode* newHead = dummy->next;
        delete dummy; // Free the dummy node
        return newHead;
    }
};

// Helper function to print the list
void printList(ListNode* head) {
    while (head != nullptr) {
        cout << head->val << " ";
        head = head->next;
    }
    cout << endl;
}

// Main function to test the solution
int main() {
    // Create a linked list: 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6
    ListNode* head = new ListNode(1);
    head->next = new ListNode(2);
    head->next->next = new ListNode(6);
    head->next->next->next = new ListNode(3);
    head->next->next->next->next = new ListNode(4);
    head->next->next->next->next->next = new ListNode(5);
    head->next->next->next->next->next->next = new ListNode(6);
    
    // Print the original list
    cout << "Original list: ";
    printList(head);
    
    // Remove elements with value 6
    Solution solution;
    head = solution.removeElements(head, 6);
    
    // Print the modified list
    cout << "Modified list: ";
    printList(head);
    
    return 0;
}

Complexity Analysis

The time complexity of this approach is O(n), where n is the number of nodes in the linked list. This is because we traverse the list once. The space complexity is O(1) as we are using only a few extra pointers and not any additional data structures.

Edge Cases

Consider the following edge cases:

These cases are handled by the algorithm as it traverses the entire list and adjusts pointers accordingly.

Testing

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

Use a variety of test cases to ensure the solution handles all scenarios correctly.

Thinking and Problem-Solving Tips

When approaching such problems, consider the following tips:

Conclusion

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

Additional Resources

For further reading and practice, consider the following resources: