💡 Recursion provides a clean, elegant way to think about the problem by handling one M+N segment at a time, but beginners must be careful with base cases and stack depth.
Intuition
Recursively skip M nodes, then delete N nodes by adjusting pointers, and call the function again on the remaining list.
Algorithm
- Base case: if head is null, return null.
- Skip M nodes by moving head pointer forward M-1 times.
- Delete next N nodes by moving a temporary pointer forward N times.
- Recursively call the function on the node after deleted nodes and link it back.
💡 Recursion breaks the problem into smaller identical subproblems, but requires careful pointer management.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def delete_n_after_m_recursive(head, M, N):
if not head:
return None
current = head
# Skip M nodes
for _ in range(1, M):
if current is None:
return head
current = current.next
if current is None:
return head
# Delete N nodes
temp = current.next
for _ in range(N):
if temp is None:
break
temp = temp.next
current.next = delete_n_after_m_recursive(temp, M, N)
return head
# Driver code
if __name__ == '__main__':
nodes = [ListNode(i) for i in range(1, 11)]
for i in range(9):
nodes[i].next = nodes[i+1]
head = nodes[0]
M, N = 2, 3
new_head = delete_n_after_m_recursive(head, M, N)
curr = new_head
res = []
while curr:
res.append(curr.val)
curr = curr.next
print(res) # Expected: [1, 2, 6, 7]
Line Notes
if not head:Base case: empty list returns None
for _ in range(1, M):Skip M nodes by moving current pointer
if current is None:Check if end reached while skipping
for _ in range(N):Delete N nodes by moving temp pointer
current.next = delete_n_after_m_recursive(temp, M, N)Recursive call on remaining list after deletion
return headReturn modified list head
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; this.next = null; }
}
public class Solution {
public static ListNode deleteNAfterMRecursive(ListNode head, int M, int N) {
if (head == null) return null;
ListNode current = head;
// Skip M nodes
for (int i = 1; i < M && current != null; i++) {
current = current.next;
}
if (current == null) return head;
// Delete N nodes
ListNode temp = current.next;
for (int i = 0; i < N && temp != null; i++) {
temp = temp.next;
}
current.next = deleteNAfterMRecursive(temp, M, N);
return head;
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode curr = head;
for (int i = 2; i <= 10; i++) {
curr.next = new ListNode(i);
curr = curr.next;
}
int M = 2, N = 3;
ListNode newHead = deleteNAfterMRecursive(head, M, N);
curr = newHead;
while (curr != null) {
System.out.print(curr.val + " ");
curr = curr.next;
}
// Expected output: 1 2 6 7
}
}
Line Notes
if (head == null) return null;Base case for recursion termination
for (int i = 1; i < M && current != null; i++)Skip M nodes carefully
if (current == null) return head;Return if end reached while skipping
for (int i = 0; i < N && temp != null; i++)Move temp pointer N nodes ahead to delete
current.next = deleteNAfterMRecursive(temp, M, N);Recursive call on remaining list
return head;Return modified list head
#include <iostream>
using namespace std;
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
ListNode* deleteNAfterMRecursive(ListNode* head, int M, int N) {
if (!head) return nullptr;
ListNode* current = head;
for (int i = 1; i < M && current != nullptr; i++) {
current = current->next;
}
if (current == nullptr) return head;
ListNode* temp = current->next;
for (int i = 0; i < N && temp != nullptr; i++) {
ListNode* toDelete = temp;
temp = temp->next;
delete toDelete;
}
current->next = deleteNAfterMRecursive(temp, M, N);
return head;
}
int main() {
ListNode* head = new ListNode(1);
ListNode* curr = head;
for (int i = 2; i <= 10; i++) {
curr->next = new ListNode(i);
curr = curr->next;
}
int M = 2, N = 3;
head = deleteNAfterMRecursive(head, M, N);
curr = head;
while (curr) {
cout << curr->val << " ";
curr = curr->next;
}
// Expected output: 1 2 6 7
return 0;
}
Line Notes
if (!head) return nullptr;Base case for recursion
for (int i = 1; i < M && current != nullptr; i++)Skip M nodes carefully
if (current == nullptr) return head;Return if end reached while skipping
delete toDelete;Free memory of deleted nodes
current->next = deleteNAfterMRecursive(temp, M, N);Recursive call on remaining list
return head;Return modified list head
class ListNode {
constructor(val = 0, next = null) {
this.val = val;
this.next = next;
}
}
function deleteNAfterMRecursive(head, M, N) {
if (head === null) return null;
let current = head;
for (let i = 1; i < M && current !== null; i++) {
current = current.next;
}
if (current === null) return head;
let temp = current.next;
for (let i = 0; i < N && temp !== null; i++) {
temp = temp.next;
}
current.next = deleteNAfterMRecursive(temp, M, N);
return head;
}
// Driver code
const nodes = [];
for (let i = 1; i <= 10; i++) {
nodes.push(new ListNode(i));
}
for (let i = 0; i < 9; i++) {
nodes[i].next = nodes[i + 1];
}
const M = 2, N = 3;
const newHead = deleteNAfterMRecursive(nodes[0], M, N);
let curr = newHead;
const res = [];
while (curr !== null) {
res.push(curr.val);
curr = curr.next;
}
console.log(res); // Expected: [1, 2, 6, 7]
Line Notes
if (head === null) return null;Base case for recursion
for (let i = 1; i < M && current !== null; i++)Skip M nodes carefully
if (current === null) return head;Return if end reached while skipping
for (let i = 0; i < N && temp !== null; i++)Move temp pointer N nodes ahead to delete
current.next = deleteNAfterMRecursive(temp, M, N);Recursive call on remaining list
return head;Return modified list head
TimeO(n)
SpaceO(n/M) due to recursion stack
Each recursive call processes M+N nodes, so total calls ~ n/(M+N). Each call does O(M+N) work, total O(n). Stack depth depends on number of segments.
💡 For large lists, recursion depth can be large and risk stack overflow, but for moderate sizes this is acceptable.
Interview Verdict: Accepted
Elegant and accepted, but recursion depth may be a concern in some environments.