Facebook Pixel

3217. Delete Nodes From Linked List Present in Array


Problem Description

You are given an array of integers nums and the head of a linked list. The task is to return the head of the modified linked list after removing all nodes from the linked list that have a value that exists in nums.

Intuition

To efficiently remove nodes with values present in nums, we leverage a hash table to store all the values from nums for quick lookup. We utilize a dummy node to simplify edge cases, such as when the head itself needs to be removed.

We iterate through the linked list, and for each node, we check if its value exists in the hash table. If the value is present, we adjust the current node's pointer to skip the node with that value, effectively removing it from the list. If the value is not present, we move the pointer to the next node. This ensures that only nodes with values not in nums remain in the list. Finally, we return the next node after the dummy node, which represents the head of the modified list.

Learn more about Linked List patterns.

Solution Approach

Solution 1: Hash Table

The solution uses a hash table to efficiently remove unwanted nodes from the linked list. Here is a step-by-step breakdown of the approach:

  1. Create a Hash Table: Convert the array nums into a set, s, so that we can quickly check if a node's value is one that needs to be removed. This conversion operation takes O(n) time, where n is the size of nums.

  2. Initialize a Dummy Node: Define a dummy node dummy that points to the head of the linked list. The dummy node is utilized to handle edge cases smoothly, like when the head node itself needs to be removed.

  3. Traverse the Linked List: Start traversing the linked list from the dummy node. Use a pointer pre, initialized to the dummy, to keep track of the node we're currently examining.

    • Check and Remove Node: As we visit each node, check if the value of the next node (pre.next.val) exists in the set s.
      • If it does, update pre.next to skip over the unwanted node (pre.next = pre.next.next), effectively removing it from the list.
      • If it doesn't, simply advance the pre pointer to the next node.
  4. Return the Modified List: After traversing the list, the linked list is now stripped of nodes whose values exist in nums. Return dummy.next, which points to the head of the modified list.

The code implements this approach concisely:

class Solution:
    def modifiedList(
        self, nums: List[int], head: Optional[ListNode]
    ) -> Optional[ListNode]:
        s = set(nums)
        pre = dummy = ListNode(next=head)
        while pre.next:
            if pre.next.val in s:
                pre.next = pre.next.next
            else:
                pre = pre.next
        return dummy.next

This method runs at O(m + n) time complexity, where m is the length of the linked list and n is the length of nums, with an O(n) space complexity due to the hash table.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through an example where nums = [2, 3] and the linked list has the values 1 -> 2 -> 3 -> 4.

  1. Initial Setup:

    • Create a set from nums: s = {2, 3} for fast look-up.
    • Initialize a dummy node dummy which points to the head of the linked list. This will help manage edge cases.
    • Set pointer pre to dummy.
  2. Traverse the Linked List:

    • Start at the node with value 1 (which is dummy.next):

      • Check if 1 is in the set s. It is not.
      • Move pre to the next node (value 1).
    • Move to the next node with value 2:

      • 2 is in set s.
      • Remove this node by setting pre.next to pre.next.next, skipping node 2.
      • The list now looks like 1 -> 3 -> 4.
    • Move to the next node (after 1), now 3:

      • 3 is in set s.
      • Remove this node by setting pre.next to pre.next.next, skipping node 3.
      • The list is now 1 -> 4.
    • Move to the next node (after 1), now 4:

      • 4 is not in set s.
      • Move pre to this node (value 4).
  3. Finished Traversal:

    • The final linked list is 1 -> 4.
  4. Return the Modified List:

    • Return dummy.next which points to 1, the head of the modified linked list.

The final output is the list 1 -> 4, with nodes containing values from nums removed.

Solution Implementation

1from typing import List, Optional
2
3# Definition for singly-linked list.
4class ListNode:
5    def __init__(self, val=0, next=None):
6        self.val = val
7        self.next = next
8
9class Solution:
10    def modifiedList(self, nums: List[int], head: Optional[ListNode]) -> Optional[ListNode]:
11        # Convert list of numbers to a set for O(1) average-time complexity lookups
12        value_set = set(nums)
13      
14        # Create a dummy node that points to the head of the list
15        dummy = ListNode(0)
16        dummy.next = head
17      
18        # Use 'curr' to traverse and 'prev' for maintaining the connection of nodes
19        prev = dummy
20        curr = head
21      
22        # Traverse the linked list
23        while curr:
24            # If the value of the current node is in the set, remove the node
25            if curr.val in value_set:
26                prev.next = curr.next
27            else:
28                # Move 'prev' to 'curr' only if no deletion occurs
29                prev = curr
30          
31            # Always move 'curr' to the next node in the list
32            curr = curr.next
33      
34        # Return the head of the modified list
35        return dummy.next
36
1import java.util.HashSet;
2import java.util.Set;
3
4// Definition for singly-linked list.
5public class ListNode {
6    int val;
7    ListNode next;
8
9    // Default constructor
10    ListNode() {}
11  
12    // Constructor with value initialization
13    ListNode(int val) { 
14        this.val = val; 
15    }
16  
17    // Constructor with value and next node initialization
18    ListNode(int val, ListNode next) { 
19        this.val = val; 
20        this.next = next; 
21    }
22}
23
24class Solution {
25    // Method to modify the linked list by removing nodes that have values present in the nums array
26    public ListNode modifiedList(int[] nums, ListNode head) {
27        Set<Integer> valueSet = new HashSet<>();
28      
29        // Add all elements of the array nums to a HashSet for quick lookup
30        for (int number : nums) {
31            valueSet.add(number);
32        }
33      
34        // Create a dummy node to simplify edge cases where removal is needed at the head
35        ListNode dummy = new ListNode(0, head);
36      
37        // Iterate through the linked list using 'pre' as a pointer to the node before the current
38        for (ListNode pre = dummy; pre.next != null;) {
39            // If the current node's value is in the set, remove it by skipping over it
40            if (valueSet.contains(pre.next.val)) {
41                pre.next = pre.next.next;
42            } else {
43                // Otherwise, move the 'pre' pointer to the next node
44                pre = pre.next;
45            }
46        }
47      
48        // Return the new head of the list, which is next to the dummy node
49        return dummy.next;
50    }
51}
52
1#include <vector>
2#include <unordered_set>
3
4/**
5 * Definition for singly-linked list.
6 */
7struct ListNode {
8    int val;            // Value of the list node
9    ListNode *next;     // Pointer to the next list node
10  
11    // Default constructor initializing with zero and nullptr
12    ListNode() : val(0), next(nullptr) {}
13  
14    // Constructor to initialize node with a given value and nullptr
15    ListNode(int x) : val(x), next(nullptr) {}
16  
17    // Constructor to initialize node with a given value and pointer to the next node
18    ListNode(int x, ListNode *next) : val(x), next(next) {}
19};
20
21class Solution {
22public:
23    /**
24     * This function modifies the input linked list by removing nodes that contain
25     * values present in the given vector `nums`.
26     *
27     * @param nums Vector of integers whose values need to be removed from the list.
28     * @param head Pointer to the head of the singly-linked list.
29     * @return Pointer to the head of the modified linked list.
30     */
31    ListNode* modifiedList(std::vector<int>& nums, ListNode* head) {
32        // Create a set with elements from nums for efficient lookup
33        std::unordered_set<int> values_set(nums.begin(), nums.end());
34
35        // Dummy node to handle edge cases easily (like deletion of the head node)
36        ListNode* dummy = new ListNode(0, head);
37
38        // Pointer to traverse the list, initially pointing to dummy node
39        ListNode* prev = dummy;
40
41        // Traverse the linked list
42        while (prev->next) {
43            // If the next node's value is in the set, skip the node by changing pointers
44            if (values_set.count(prev->next->val)) {
45                prev->next = prev->next->next;
46            } else {
47                // Otherwise, move prev pointer forward
48                prev = prev->next;
49            }
50        }
51
52        // Return the head of the modified list, which is next of dummy node
53        return dummy->next;
54    }
55};
56
1/**
2 * Definition for singly-linked list node.
3 */
4class ListNode {
5    val: number;
6    next: ListNode | null;
7
8    constructor(val?: number, next?: ListNode | null) {
9        this.val = (val === undefined ? 0 : val);
10        this.next = (next === undefined ? null : next);
11    }
12}
13
14/**
15 * Modifies the linked list based on the values present in the nums array.
16 * Removes nodes from the linked list that contain values present in the nums array.
17 * 
18 * @param nums - Array of numbers to be removed from the linked list.
19 * @param head - Head of the original linked list.
20 * @returns - New head of the modified linked list.
21 */
22function modifiedList(nums: number[], head: ListNode | null): ListNode | null {
23    // Create a set from nums to quickly check for existence.
24    const valuesToRemove: Set<number> = new Set(nums);
25
26    // Initialize a dummy node that points to the head.
27    const dummy: ListNode = new ListNode(0, head);
28
29    // Iterate through the linked list.
30    for (let currentPointer = dummy; currentPointer.next; ) {
31        // If the value of the next node exists in the set, remove the node.
32        if (valuesToRemove.has(currentPointer.next.val)) {
33            currentPointer.next = currentPointer.next.next;
34        } else {
35            // Otherwise, move the pointer forward.
36            currentPointer = currentPointer.next;
37        }
38    }
39  
40    // Return the new head of the list, which is the next of the dummy node.
41    return dummy.next;
42}
43

Time and Space Complexity

The time complexity of the code is O(n + m), where n is the length of the array nums and m is the length of the linked list head. This is because creating the set s from nums requires O(n) time, and iterating through the linked list requires O(m) time.

The space complexity of the code is O(n). This stems from the creation of the set s, which stores the unique elements from the array nums, requiring O(n) additional space.

Learn more about how to find time and space complexity quickly.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!


Load More