2181. Merge Nodes in Between Zeros
Problem Description
You are given a linked list that starts and ends with nodes having value 0
. The list contains groups of non-zero integers separated by 0
s.
Your task is to merge each group of non-zero values between consecutive 0
s into a single node. The value of this new node should be the sum of all the non-zero values in that group. The resulting linked list should not contain any 0
s.
For example, if you have a linked list like: 0 → 3 → 1 → 0 → 4 → 5 → 2 → 0
, the output should be: 4 → 11
- The first group between zeros is
[3, 1]
, which sums to4
- The second group between zeros is
[4, 5, 2]
, which sums to11
The algorithm works by:
- Starting from the node after the first
0
(since we know the list starts with0
) - Accumulating the sum of values until we encounter another
0
- When we hit a
0
, we create a new node with the accumulated sum and reset the sum to0
- We continue this process until we reach the end of the list
- The dummy node technique is used to simplify handling the head of the result list
The time complexity is O(n)
where n
is the number of nodes in the original list, and the space complexity is O(1)
excluding the output list.
Intuition
The key insight is recognizing that 0
s act as delimiters that divide the linked list into segments. Since we need to merge all nodes between consecutive 0
s, we can think of this as a "group and sum" problem.
When traversing a linked list, we naturally process nodes one by one. As we encounter non-zero values, we can accumulate them into a running sum. The moment we hit a 0
, we know we've reached the end of a group - this is our signal to create a new node with the accumulated sum and reset our accumulator for the next group.
The pattern becomes clear: we're essentially in one of two states as we traverse:
- Accumulating state: When we see non-zero values, add them to our sum
- Creating state: When we see a
0
, create a new node with the current sum and reset
Since the list starts with a 0
, we can skip it and begin processing from the second node. Similarly, since it ends with a 0
, our last accumulated sum will naturally be added when we encounter that final 0
.
The use of a dummy node is a common linked list technique that eliminates special handling for the head of the result list. Instead of checking whether we're creating the first node or a subsequent node, we can uniformly append all new nodes to tail.next
and return dummy.next
at the end.
This approach emerges naturally when we think about the problem sequentially - process each element, accumulate when needed, and output when we reach a boundary marker (the 0
s).
Learn more about Linked List patterns.
Solution Approach
We implement the solution using a single-pass traversal with the following components:
Data Structures:
dummy
: A dummy head node that simplifies list constructiontail
: A pointer that tracks where to append the next merged nodes
: An accumulator variable to store the running sumcur
: A pointer to traverse the original linked list
Algorithm Steps:
-
Initialize the tracking variables:
dummy = tail = ListNode() s = 0 cur = head.next # Skip the first 0
We create a dummy node and set
tail
to point to it. Start traversal fromhead.next
since we know the first node is always0
. -
Traverse and process each node:
while cur: if cur.val: s += cur.val else: tail.next = ListNode(s) tail = tail.next s = 0 cur = cur.next
For each node in the list:
- If the value is non-zero (
cur.val
is truthy), add it to our accumulators
- If the value is
0
, we've reached the end of a segment:- Create a new node with value
s
- Attach it to the result list via
tail.next
- Move
tail
forward to this new node - Reset
s
to0
for the next segment
- Create a new node with value
- Move to the next node with
cur = cur.next
- If the value is non-zero (
-
Return the result:
return dummy.next
Since
dummy
is a placeholder, the actual result list starts atdummy.next
.
Why this works:
- Every segment between
0
s gets accumulated intos
- When we hit a
0
, we know the segment is complete and create the merged node - The dummy node pattern ensures we don't need special logic for the first merged node
- Since the list ends with
0
, the last segment is automatically handled when we encounter the final0
The time complexity is O(n)
where n
is the number of nodes, as we visit each node exactly once. The space complexity is O(1)
for the auxiliary variables (not counting the output list).
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through a small example: 0 → 2 → 3 → 0 → 5 → 0
Initial Setup:
- Create
dummy
andtail
pointing to a new ListNode - Set
s = 0
(accumulator for sum) - Set
cur = head.next
(pointing to node with value 2, skipping the first 0) - Result list so far:
dummy →
Step 1: Process node with value 2
cur.val = 2
(non-zero)- Add to sum:
s = 0 + 2 = 2
- Move forward:
cur
now points to node with value 3 - Result list unchanged:
dummy →
Step 2: Process node with value 3
cur.val = 3
(non-zero)- Add to sum:
s = 2 + 3 = 5
- Move forward:
cur
now points to node with value 0 - Result list unchanged:
dummy →
Step 3: Process node with value 0
cur.val = 0
(delimiter found!)- Create new node with accumulated sum:
ListNode(5)
- Attach to result:
tail.next = ListNode(5)
- Move tail forward:
tail
now points to the new node - Reset sum:
s = 0
- Move forward:
cur
now points to node with value 5 - Result list now:
dummy → 5 →
Step 4: Process node with value 5
cur.val = 5
(non-zero)- Add to sum:
s = 0 + 5 = 5
- Move forward:
cur
now points to final node with value 0 - Result list unchanged:
dummy → 5 →
Step 5: Process final node with value 0
cur.val = 0
(delimiter found!)- Create new node with accumulated sum:
ListNode(5)
- Attach to result:
tail.next = ListNode(5)
- Move tail forward:
tail
now points to the new node - Reset sum:
s = 0
- Move forward:
cur
becomesNone
(end of list) - Result list now:
dummy → 5 → 5 →
Step 6: Return result
- Return
dummy.next
, which points to the first real node - Final output:
5 → 5
The algorithm successfully merged [2, 3]
into 5
and [5]
into 5
, removing all zeros from the original list.
Solution Implementation
1# Definition for singly-linked list.
2# class ListNode:
3# def __init__(self, val=0, next=None):
4# self.val = val
5# self.next = next
6
7from typing import Optional
8
9class Solution:
10 def mergeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]:
11 # Create a dummy node to simplify list construction
12 # tail pointer will be used to build the result list
13 dummy_head = ListNode()
14 tail = dummy_head
15
16 # Initialize sum accumulator for values between zeros
17 current_sum = 0
18
19 # Start from the node after the first zero
20 current = head.next
21
22 # Traverse the linked list
23 while current:
24 if current.val != 0:
25 # Accumulate non-zero values
26 current_sum += current.val
27 else:
28 # When we encounter a zero, create a new node with the accumulated sum
29 tail.next = ListNode(current_sum)
30 tail = tail.next
31 # Reset the sum for the next segment
32 current_sum = 0
33
34 # Move to the next node
35 current = current.next
36
37 # Return the head of the merged list (skip dummy node)
38 return dummy_head.next
39
1/**
2 * Definition for singly-linked list.
3 * public class ListNode {
4 * int val;
5 * ListNode next;
6 * ListNode() {}
7 * ListNode(int val) { this.val = val; }
8 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
9 * }
10 */
11class Solution {
12 public ListNode mergeNodes(ListNode head) {
13 // Create a dummy node to simplify list construction
14 ListNode dummyHead = new ListNode();
15
16 // Initialize sum accumulator for values between zeros
17 int sum = 0;
18
19 // Pointer to track the tail of the result list
20 ListNode currentTail = dummyHead;
21
22 // Iterate through the list starting from the node after the first zero
23 for (ListNode currentNode = head.next; currentNode != null; currentNode = currentNode.next) {
24 if (currentNode.val != 0) {
25 // Accumulate non-zero values
26 sum += currentNode.val;
27 } else {
28 // When encountering a zero, create a new node with the accumulated sum
29 currentTail.next = new ListNode(sum);
30 currentTail = currentTail.next;
31
32 // Reset sum for the next segment
33 sum = 0;
34 }
35 }
36
37 // Return the head of the merged list (skip dummy node)
38 return dummyHead.next;
39 }
40}
41
1/**
2 * Definition for singly-linked list.
3 * struct ListNode {
4 * int val;
5 * ListNode *next;
6 * ListNode() : val(0), next(nullptr) {}
7 * ListNode(int x) : val(x), next(nullptr) {}
8 * ListNode(int x, ListNode *next) : val(x), next(next) {}
9 * };
10 */
11class Solution {
12public:
13 /**
14 * Merges nodes between zeros in a linked list.
15 * The list starts and ends with 0, with non-zero values in between.
16 * Each segment between consecutive zeros is summed into a single node.
17 *
18 * @param head: pointer to the head of the linked list
19 * @return: pointer to the head of the merged linked list
20 */
21 ListNode* mergeNodes(ListNode* head) {
22 // Create a dummy node to simplify list construction
23 ListNode* dummyHead = new ListNode();
24 ListNode* currentTail = dummyHead;
25
26 // Initialize sum accumulator for values between zeros
27 int currentSum = 0;
28
29 // Start from the node after the first zero
30 for (ListNode* currentNode = head->next; currentNode != nullptr; currentNode = currentNode->next) {
31 if (currentNode->val != 0) {
32 // Accumulate non-zero values
33 currentSum += currentNode->val;
34 } else {
35 // When encountering a zero, create a new node with the accumulated sum
36 currentTail->next = new ListNode(currentSum);
37 currentTail = currentTail->next;
38
39 // Reset the sum for the next segment
40 currentSum = 0;
41 }
42 }
43
44 // Return the actual head (skip the dummy node)
45 return dummyHead->next;
46 }
47};
48
1/**
2 * Definition for singly-linked list.
3 * class ListNode {
4 * val: number
5 * next: ListNode | null
6 * constructor(val?: number, next?: ListNode | null) {
7 * this.val = (val===undefined ? 0 : val)
8 * this.next = (next===undefined ? null : next)
9 * }
10 * }
11 */
12
13/**
14 * Merges nodes between zeros in a linked list by summing their values.
15 * The input list starts and ends with 0, with non-zero values in between.
16 * Creates a new list where each node contains the sum of values between consecutive zeros.
17 *
18 * @param head - The head of the input linked list
19 * @returns The head of the merged linked list
20 */
21function mergeNodes(head: ListNode | null): ListNode | null {
22 // Create a dummy node to simplify list construction
23 const dummyNode: ListNode = new ListNode();
24
25 // Pointer to track the tail of the result list
26 let tailPointer: ListNode = dummyNode;
27
28 // Accumulator for summing values between zeros
29 let sum: number = 0;
30
31 // Iterate through the list starting from the node after head (skip first zero)
32 for (let currentNode: ListNode | null = head!.next; currentNode !== null; currentNode = currentNode.next) {
33 if (currentNode.val !== 0) {
34 // Add non-zero value to the running sum
35 sum += currentNode.val;
36 } else {
37 // When encountering a zero, create a new node with the accumulated sum
38 tailPointer.next = new ListNode(sum);
39 tailPointer = tailPointer.next;
40
41 // Reset sum for the next segment
42 sum = 0;
43 }
44 }
45
46 // Return the actual head of the result list (skip dummy node)
47 return dummyNode.next;
48}
49
Time and Space Complexity
Time Complexity: O(n)
, where n
is the total number of nodes in the input linked list.
The algorithm traverses the entire linked list exactly once using a single while loop. For each node in the original list, we perform constant time operations:
- Checking if
cur.val
is non-zero:O(1)
- Adding to sum
s
:O(1)
- Creating a new node when encountering a zero:
O(1)
- Moving the pointer
cur
forward:O(1)
Since we visit each of the n
nodes exactly once and perform O(1)
operations per node, the overall time complexity is O(n)
.
Space Complexity: O(m)
, where m
is the number of segments (or the number of zeros minus one) in the input linked list.
However, since in the worst case m
can be proportional to n
(when zeros and non-zeros alternate), the space complexity can also be expressed as O(n)
.
The space is used for:
- Creating new
ListNode
objects for the result linked list. The number of new nodes created equals the number of segments between zeros. - A few constant space variables (
dummy
,tail
,s
,cur
):O(1)
The algorithm does not use recursion or any additional data structures that scale with input size beyond the output linked list itself, so the space complexity is determined by the size of the output, which is O(n)
in the worst case.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Skip the Initial Zero
A common mistake is starting the traversal from head
instead of head.next
. Since the problem guarantees the list starts with 0
, we must skip it to avoid processing it as part of the first group.
Incorrect:
current = head # Wrong! This includes the first 0 while current: if current.val != 0: current_sum += current.val # ...
Correct:
current = head.next # Skip the first 0
while current:
if current.val != 0:
current_sum += current.val
# ...
2. Creating Empty Nodes for Consecutive Zeros
If the input contains consecutive zeros (like 0 → 3 → 0 → 0 → 5 → 0
), you might accidentally create nodes with value 0
in the result, which violates the requirement that the output should not contain any zeros.
Solution: Add a check before creating a new node:
if current.val == 0:
if current_sum > 0: # Only create node if we have accumulated values
tail.next = ListNode(current_sum)
tail = tail.next
current_sum = 0
3. Not Handling Edge Cases
The code might fail on edge cases like:
- A list with only zeros:
0 → 0 → 0
- A list with a single group:
0 → 5 → 0
Solution: The current implementation handles these correctly, but always test with:
# Test edge cases # Input: 0 → 0 → 0 → 0 # Expected Output: None or empty list # Input: 0 → 5 → 0 # Expected Output: 5
4. Memory Management with the Dummy Node
Forgetting to return dummy.next
instead of dummy
itself. The dummy node is just a placeholder and should not be part of the final result.
Incorrect:
return dummy_head # Wrong! This includes the dummy node with value 0
Correct:
return dummy_head.next # Correct! Skip the dummy node
What's the output of running the following function using the following tree as input?
1def serialize(root):
2 res = []
3 def dfs(root):
4 if not root:
5 res.append('x')
6 return
7 res.append(root.val)
8 dfs(root.left)
9 dfs(root.right)
10 dfs(root)
11 return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4 StringJoiner res = new StringJoiner(" ");
5 serializeDFS(root, res);
6 return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10 if (root == null) {
11 result.add("x");
12 return;
13 }
14 result.add(Integer.toString(root.val));
15 serializeDFS(root.left, result);
16 serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2 let res = [];
3 serialize_dfs(root, res);
4 return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8 if (!root) {
9 res.push("x");
10 return;
11 }
12 res.push(root.val);
13 serialize_dfs(root.left, res);
14 serialize_dfs(root.right, res);
15}
16
Recommended Readings
Linked List Cycle Given a linked list with potentially a loop determine whether the linked list from the first node contains a cycle in it For bonus points do this with constant space Parameters nodes The first node of a linked list with potentially a loop Result Whether there is a loop contained
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!