2 minute read

Problem Statement

problem-1171

Intuition

The problem involves removing zero-sum sublists from a linked list. Use stack and hash set data structure to solve the problem.

Approach

I approach the problem by using a stack to maintain the running sum of nodes in the linked list along with the corresponding node. I also use a set (prefix_sum) to keep track of the unique running sums encountered. While iterating through the linked list, if the running sum is already in the set, I remove the subsequence with a zero sum. Finally, I connect the remaining nodes in the stack to form the modified linked list.

Complexity

  • Time complexity: O(n), where n is the number of nodes in the linked list. The algorithm iterates through the list once.

  • Space complexity: O(n), as the space required for the stack and set is proportional to the number of nodes in the linked list.

Code

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeZeroSumSublists(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode(-1)
        stack = [[dummy, 0]]
        curr = head
        prefix_sum = {0}
        while curr:
            _, total = stack[-1]
            total += curr.val
            if total in prefix_sum:
                seen = total
                while stack and stack[-1][1] != seen:
                    _, total = stack.pop()
                    prefix_sum.remove(total)
            else:
                stack.append([curr, total])
                prefix_sum.add(total)
            curr = curr.next

        for i in range(len(stack) - 1):
            stack[i][0].next = stack[i + 1][0]

        stack[-1][0].next = None

        return stack[0][0].next

Editorial Solution

Approach 1: Prefix Sum for Each Consecutive Sequence

class Solution:
    def removeZeroSumSublists(self, head: Optional[ListNode]) -> Optional[ListNode]:
        front = ListNode(0, head)
        start = front

        while start is not None:
            prefix_sum = 0
            end = start.next

            while end is not None:
                # Add end's value to the prefix sum
                prefix_sum += end.val
                # Delete zero sum consecutive sequence
                # by setting node before sequence to node after
                if prefix_sum == 0:
                    start.next = end.next
                end = end.next

            start = start.next

        return front.next
  • Time complexity: O(n^2)
  • Space complexity: O(1)

Approach 2: Prefix Sum Hash Table

implementation visited each node in the linked list twice

class Solution:
    def removeZeroSumSublists(self, head: Optional[ListNode]) -> Optional[ListNode]:
        front = ListNode(0, head)
        current = front
        prefix_sum = 0
        prefix_sum_to_node = {0: front}

        # Calculate the prefix sum for each node and add to the hashmap
        # Duplicate prefix sum values will be replaced
        while current is not None:
            prefix_sum += current.val
            prefix_sum_to_node[prefix_sum] = current
            current = current.next

        # Reset prefix sum and current
        prefix_sum = 0
        current = front

        # Delete zero sum consecutive sequences
        # by setting node before sequence to node after
        while current is not None:
            prefix_sum += current.val
            current.next = prefix_sum_to_node[prefix_sum].next
            current = current.next

        return front.next

Improved implementation

class Solution:
    def removeZeroSumSublists(self, head: Optional[ListNode]) -> Optional[ListNode]:
        front = ListNode(0, head)
        current = front
        prefix_sum = 0
        prefix_sum_to_node = {}
        while current is not None:
            # Add current's value to the prefix sum
            prefix_sum += current.val

            # If prefix_sum is already in the hashmap,
            # we have found a zero-sum sequence:
            if prefix_sum in prefix_sum_to_node:
                prev = prefix_sum_to_node[prefix_sum]
                current = prev.next

                # Delete zero sum nodes from hashmap
                # to prevent incorrect deletions from linked list
                p = prefix_sum + current.val
                while p != prefix_sum:
                    del prefix_sum_to_node[p]
                    current = current.next
                    p += current.val

                # Make connection from the node before
                # the zero sum sequence to the node after
                prev.next = current.next
            else:
                # Add new prefix_sum to hashmap
                prefix_sum_to_node[prefix_sum] = current

            # Progress to next element in list
            current = current.next

        return front.next
  • Time complexity: O(n)
  • Space complexity: O(n)