1474. Delete N Nodes After M Nodes of a Linked List 🔒
Problem Description
You have a linked list and need to modify it by keeping some nodes and removing others in a specific pattern. Given the head of the linked list and two integers m
and n
, you need to:
- Start at the head of the linked list
- Keep the first
m
nodes - Remove the next
n
nodes - Repeat steps 2 and 3 until you reach the end of the list
The process works like this: you traverse the linked list, keeping m
consecutive nodes, then deleting the next n
consecutive nodes, and continue this pattern throughout the entire list. If there are fewer nodes remaining than needed for a complete operation, you process what's available.
For example, if you have a linked list 1→2→3→4→5→6→7→8→9
with m=2
and n=3
:
- Keep nodes 1 and 2 (first
m=2
nodes) - Remove nodes 3, 4, and 5 (next
n=3
nodes) - Keep nodes 6 and 7 (next
m=2
nodes) - Remove nodes 8 and 9 (remaining nodes, less than
n=3
) - Result:
1→2→6→7
The function should return the head of the modified linked list after performing all the removal operations.
Intuition
The key insight is that we need to traverse the linked list while alternating between two operations: keeping nodes and removing nodes. This suggests a simulation approach where we directly implement what the problem asks.
Think of it like walking through a line of people where you need to keep some and skip others. You'd need two things:
- A marker to remember where you stopped keeping nodes (so you know where to reconnect after skipping)
- A way to jump over the nodes you want to remove
The natural approach is to use two pointers:
- A
pre
pointer that marks the last node we want to keep before we start removing nodes - A
cur
pointer that helps us skip over then
nodes we want to remove
The process becomes:
- Move
pre
forward bym-1
steps (sincepre
starts at a node we're keeping, we only needm-1
more steps to keepm
total nodes) - Use
cur
to find where we should reconnect after skippingn
nodes - Connect
pre.next
to the node after the removed section - Move
pre
to continue the pattern
We need to handle edge cases carefully - what if we run out of nodes while trying to keep m
nodes? What if we run out while trying to remove n
nodes? The solution handles these by checking if pointers become None
and adjusting accordingly.
This simulation approach is straightforward because it directly mirrors the problem statement - we're literally doing what the problem asks us to do, step by step.
Learn more about Linked List patterns.
Solution Approach
The solution uses a simulation approach with two pointers to traverse and modify the linked list in place.
We start by initializing pre
to point to the head of the linked list. This pointer will track the last node we keep before each deletion segment.
The main loop continues while pre
is not null:
-
Keep m nodes: Starting from the current position of
pre
, we move forwardm-1
times (sincepre
is already at the first node to keep). During this traversal, we check ifpre
becomes null - if it does, we've reached the end of the list with fewer thanm
nodes remaining, so we return the head. -
Position for deletion: After keeping
m
nodes,pre
now points to the last node we want to keep. We initializecur
to point topre
and then movecur
forwardn
times to skip over the nodes we want to delete. -
Reconnect the list: After moving
cur
forwardn
times:- If
cur
is null, it means we've reached the end while trying to delete nodes, so we setpre.next = None
to terminate the list - Otherwise, we set
pre.next = cur.next
to skip over then
nodes and connect to the remaining list
- If
-
Move to next segment: We update
pre = pre.next
to position it for the next iteration of keeping and removing nodes.
The algorithm handles edge cases naturally:
- If the list has fewer than
m
nodes from the current position, we return immediately - If there are fewer than
n
nodes to delete, we remove whatever remains - The loop terminates when
pre
becomes null, meaning we've processed the entire list
Time complexity: O(L)
where L
is the length of the linked list, as we visit each node at most once.
Space complexity: O(1)
as we only use a constant amount of extra space for the pointers.
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 walk through a small example with the linked list 1→2→3→4→5→6
where m=2
and n=1
.
Initial State:
head → 1 → 2 → 3 → 4 → 5 → 6 → None pre → 1
Iteration 1:
-
Keep
m=2
nodes: Movepre
forwardm-1=1
timepre
moves from node 1 to node 2
1 → 2 → 3 → 4 → 5 → 6 → None pre
-
Position for deletion: Set
cur = pre
and movecur
forwardn=1
timecur
starts at node 2, moves to node 3
1 → 2 → 3 → 4 → 5 → 6 → None pre cur
-
Reconnect: Since
cur
is not null, setpre.next = cur.next
pre.next
now points to node 4, effectively removing node 3
1 → 2 ──→ 4 → 5 → 6 → None pre
-
Move forward:
pre = pre.next
(pre now points to node 4)1 → 2 → 4 → 5 → 6 → None pre
Iteration 2:
-
Keep
m=2
nodes: Movepre
forwardm-1=1
timepre
moves from node 4 to node 5
1 → 2 → 4 → 5 → 6 → None pre
-
Position for deletion: Set
cur = pre
and movecur
forwardn=1
timecur
starts at node 5, moves to node 6
1 → 2 → 4 → 5 → 6 → None pre cur
-
Reconnect: Since
cur
is not null, setpre.next = cur.next
pre.next
now points to None, effectively removing node 6
1 → 2 → 4 → 5 → None pre
-
Move forward:
pre = pre.next
(pre becomes None)- Loop terminates since
pre
is None
- Loop terminates since
Final Result: 1→2→4→5
The algorithm successfully kept nodes [1,2], removed [3], kept [4,5], and removed [6], following the pattern of keeping 2 and removing 1 throughout the 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
7class Solution:
8 def deleteNodes(self, head: ListNode, m: int, n: int) -> ListNode:
9 """
10 Delete n nodes after every m nodes in a linked list.
11
12 Args:
13 head: The head of the linked list
14 m: Number of nodes to keep
15 n: Number of nodes to delete after keeping m nodes
16
17 Returns:
18 The head of the modified linked list
19 """
20 # Start from the head node
21 current = head
22
23 while current:
24 # Keep m nodes (traverse m-1 times since we're already at one node)
25 for _ in range(m - 1):
26 if current:
27 current = current.next
28
29 # If we've reached the end while keeping nodes, return the list
30 if current is None:
31 return head
32
33 # Start deleting n nodes from the next position
34 node_to_delete = current
35 for _ in range(n):
36 if node_to_delete and node_to_delete.next:
37 node_to_delete = node_to_delete.next
38
39 # Connect the last kept node to the node after deleted nodes
40 if node_to_delete:
41 current.next = node_to_delete.next
42 else:
43 current.next = None
44
45 # Move to the next set of nodes to process
46 current = current.next
47
48 return head
49
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 /**
13 * Delete N nodes after every M nodes in a linked list.
14 *
15 * @param head The head of the linked list
16 * @param m Number of nodes to keep
17 * @param n Number of nodes to delete after keeping m nodes
18 * @return The head of the modified linked list
19 */
20 public ListNode deleteNodes(ListNode head, int m, int n) {
21 // Start from the head of the list
22 ListNode currentNode = head;
23
24 // Process the list until we reach the end
25 while (currentNode != null) {
26 // Keep m nodes: traverse m-1 steps from current position
27 // (current node is already counted as the first node to keep)
28 for (int i = 0; i < m - 1 && currentNode != null; i++) {
29 currentNode = currentNode.next;
30 }
31
32 // If we've reached the end while keeping nodes, return the result
33 if (currentNode == null) {
34 return head;
35 }
36
37 // Find the position after skipping n nodes to be deleted
38 ListNode nodeToDelete = currentNode;
39 for (int i = 0; i < n && nodeToDelete != null; i++) {
40 nodeToDelete = nodeToDelete.next;
41 }
42
43 // Connect the last kept node to the node after deleted nodes
44 // If nodeToDelete is null, we've reached the end
45 currentNode.next = (nodeToDelete == null) ? null : nodeToDelete.next;
46
47 // Move to the next segment to process
48 currentNode = currentNode.next;
49 }
50
51 return head;
52 }
53}
54
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 * Delete n nodes after keeping m nodes in a linked list repeatedly
15 * @param head - pointer to the head of the linked list
16 * @param m - number of nodes to keep
17 * @param n - number of nodes to delete after keeping m nodes
18 * @return pointer to the head of the modified linked list
19 */
20 ListNode* deleteNodes(ListNode* head, int m, int n) {
21 ListNode* currentNode = head;
22
23 while (currentNode != nullptr) {
24 // Move forward m-1 steps to reach the last node to keep
25 // We need m-1 steps because currentNode is already at position 1
26 for (int i = 0; i < m - 1 && currentNode != nullptr; ++i) {
27 currentNode = currentNode->next;
28 }
29
30 // If we've reached the end while keeping nodes, return the result
31 if (currentNode == nullptr) {
32 return head;
33 }
34
35 // Start deleting n nodes from the next position
36 ListNode* nodeToDelete = currentNode;
37 for (int i = 0; i < n && nodeToDelete != nullptr; ++i) {
38 nodeToDelete = nodeToDelete->next;
39 }
40
41 // Connect the last kept node to the node after deleted nodes
42 // If nodeToDelete is not null, skip to its next; otherwise, set to null
43 currentNode->next = (nodeToDelete != nullptr) ? nodeToDelete->next : nullptr;
44
45 // Move to the next segment to process
46 currentNode = currentNode->next;
47 }
48
49 return head;
50 }
51};
52
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 * Deletes n nodes after keeping m nodes in a linked list pattern.
15 * The pattern repeats: keep m nodes, delete n nodes, keep m nodes, delete n nodes, etc.
16 *
17 * @param head - The head of the linked list
18 * @param m - Number of nodes to keep in each iteration
19 * @param n - Number of nodes to delete after keeping m nodes
20 * @returns The modified linked list head
21 */
22function deleteNodes(head: ListNode | null, m: number, n: number): ListNode | null {
23 // Start with the head as the previous pointer
24 let previousNode: ListNode | null = head;
25
26 // Continue processing while there are nodes to process
27 while (previousNode) {
28 // Move forward m-1 times to reach the last node to keep
29 // (we're already at the first node to keep)
30 for (let i = 0; i < m - 1 && previousNode; i++) {
31 previousNode = previousNode.next;
32 }
33
34 // If we've reached the end of the list, stop processing
35 if (!previousNode) {
36 break;
37 }
38
39 // Start from the last kept node to find where to reconnect
40 let currentNode: ListNode | null = previousNode;
41
42 // Skip n nodes that need to be deleted
43 for (let i = 0; i < n && currentNode; i++) {
44 currentNode = currentNode.next;
45 }
46
47 // Connect the last kept node to the node after the deleted section
48 // Use optional chaining and nullish coalescing for safety
49 previousNode.next = currentNode?.next ?? null;
50
51 // Move to the next section to process
52 previousNode = previousNode.next;
53 }
54
55 return head;
56}
57
Time and Space Complexity
Time Complexity: O(n)
, where n
is the total number of nodes in the linked list.
The algorithm traverses the linked list once from start to finish. In each iteration:
- It moves
m-1
steps to keepm
nodes - Then moves
n
steps to skipn
nodes - Each node is visited at most once during the entire process
Since we process each node a constant number of times (either keeping it or skipping it), the total time complexity is linear with respect to the number of nodes.
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space:
- Two pointer variables (
pre
andcur
) to track positions in the linked list - Loop counter variables (implicit in the
for
loops)
No additional data structures are created that scale with the input size. The modifications are done in-place by adjusting the next
pointers of the existing nodes, without creating new nodes or using recursion that would consume stack space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Off-by-One Error When Traversing to Keep Nodes
A common mistake is incorrectly counting the nodes to keep. Since current
already points to the first node in the "keep" segment, we should only move forward m-1
times, not m
times.
Incorrect approach:
# Wrong: This moves m times, effectively keeping m+1 nodes
for _ in range(m): # ❌ Wrong!
if current:
current = current.next
Correct approach:
# Correct: Move m-1 times since current is already at the first node to keep
for _ in range(m - 1): # ✓ Correct
if current:
current = current.next
Pitfall 2: Not Properly Handling the Deletion Traversal
When deleting nodes, developers often confuse whether to traverse n
times or n+1
times. The key is understanding that we need to reach the last node to delete, then connect to the node after it.
Incorrect approach:
# Wrong: This doesn't position correctly for reconnection
node_to_delete = current.next # Start from next node
for _ in range(n - 1): # ❌ Traverses one less than needed
if node_to_delete:
node_to_delete = node_to_delete.next
Correct approach:
# Correct: Start from current and traverse n times to skip n nodes
node_to_delete = current
for _ in range(n): # ✓ Traverse exactly n times
if node_to_delete and node_to_delete.next:
node_to_delete = node_to_delete.next
Pitfall 3: Forgetting to Handle Edge Cases
Failing to check for null pointers during traversal can cause runtime errors when the list ends mid-operation.
Incorrect approach:
# Wrong: No null checks, will crash if list is shorter
for _ in range(m - 1):
current = current.next # ❌ No null check!
# Later in deletion
for _ in range(n):
node_to_delete = node_to_delete.next # ❌ No null check!
Correct approach:
# Correct: Always check for null before accessing next
for _ in range(m - 1):
if current: # ✓ Null check
current = current.next
# In deletion
for _ in range(n):
if node_to_delete and node_to_delete.next: # ✓ Null check
node_to_delete = node_to_delete.next
Complete Corrected Solution:
class Solution:
def deleteNodes(self, head: ListNode, m: int, n: int) -> ListNode:
current = head
while current:
# Keep m nodes (move m-1 times)
for i in range(m - 1):
if not current:
return head
current = current.next
if not current:
return head
# Delete n nodes
temp = current
for i in range(n):
if temp and temp.next:
temp = temp.next
else:
break
# Reconnect the list
if temp:
current.next = temp.next
else:
current.next = None
# Move to next segment
current = current.next
return head
Which algorithm should you use to find a node that is close to the root of the tree?
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!