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
mnodes - Remove the next
nnodes - 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=2nodes) - Remove nodes 3, 4, and 5 (next
n=3nodes) - Keep nodes 6 and 7 (next
m=2nodes) - 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
prepointer that marks the last node we want to keep before we start removing nodes - A
curpointer that helps us skip over thennodes we want to remove
The process becomes:
- Move
preforward bym-1steps (sinceprestarts at a node we're keeping, we only needm-1more steps to keepmtotal nodes) - Use
curto find where we should reconnect after skippingnnodes - Connect
pre.nextto the node after the removed section - Move
preto 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-1times (sincepreis already at the first node to keep). During this traversal, we check ifprebecomes null - if it does, we've reached the end of the list with fewer thanmnodes remaining, so we return the head. -
Position for deletion: After keeping
mnodes,prenow points to the last node we want to keep. We initializecurto point topreand then movecurforwardntimes to skip over the nodes we want to delete. -
Reconnect the list: After moving
curforwardntimes:- If
curis null, it means we've reached the end while trying to delete nodes, so we setpre.next = Noneto terminate the list - Otherwise, we set
pre.next = cur.nextto skip over thennodes and connect to the remaining list
- If
-
Move to next segment: We update
pre = pre.nextto position it for the next iteration of keeping and removing nodes.
The algorithm handles edge cases naturally:
- If the list has fewer than
mnodes from the current position, we return immediately - If there are fewer than
nnodes to delete, we remove whatever remains - The loop terminates when
prebecomes 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=2nodes: Movepreforwardm-1=1timepremoves from node 1 to node 2
1 → 2 → 3 → 4 → 5 → 6 → None pre -
Position for deletion: Set
cur = preand movecurforwardn=1timecurstarts at node 2, moves to node 3
1 → 2 → 3 → 4 → 5 → 6 → None pre cur -
Reconnect: Since
curis not null, setpre.next = cur.nextpre.nextnow 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=2nodes: Movepreforwardm-1=1timepremoves from node 4 to node 5
1 → 2 → 4 → 5 → 6 → None pre -
Position for deletion: Set
cur = preand movecurforwardn=1timecurstarts at node 5, moves to node 6
1 → 2 → 4 → 5 → 6 → None pre cur -
Reconnect: Since
curis not null, setpre.next = cur.nextpre.nextnow points to None, effectively removing node 6
1 → 2 → 4 → 5 → None pre -
Move forward:
pre = pre.next(pre becomes None)- Loop terminates since
preis 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
491/**
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}
541/**
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};
521/**
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}
57Time 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-1steps to keepmnodes - Then moves
nsteps to skipnnodes - 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 (
preandcur) to track positions in the linked list - Loop counter variables (implicit in the
forloops)
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
What is the best way of checking if an element exists in a sorted array once in terms of time complexity? Select the best that applies.
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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!