004 — Reverse Linked List (Iterative AND Recursive)¶
Source¶
- Original source: Classic capstone interview problem (presented two ways in one sitting)
- Platform: LeetCode — https://leetcode.com/problems/reverse-linked-list/
- Topic: linked lists + recursion
- Difficulty: Easy
- Pattern: Pointer surgery — iterative three-pointer AND recursive rewiring
- Basic idea inherited from: এটা একটা cross-topic capstone — এখানে মেশে 04 linked lists (node-এর
nextarrow ঘোরানো, pointer surgery) আর 06 recursion (একই কাজকে call stack দিয়ে করা: লেজ আগে উল্টাও, তারপর নিজের link ঠিক করো)। mock-এ এর শর্ত: এক বসায় দুই রূপই লেখা।
1. Problem in simple words¶
একটা singly linked list দেওয়া — কতগুলো node, প্রতিটা পরেরটার দিকে next arrow দিয়ে তাকিয়ে। সব arrow উল্টে দাও, যাতে শেষ node প্রথম হয়, প্রথমটা শেষ। নতুন head return করো। এই capstone-এ তোমাকে দুইভাবে করতে হবে — একবার iterative (লুপ), একবার recursive (call stack)।
2. Which basic idea does this inherit from?¶
এই problem দুটো আগের chapter-এর tool জোড়া দেয়:
- 04 linked lists থেকে: এক জোড়া node-এর arrow ঘোরানো (
a -> bকেa <- b), তিন pointer দিয়ে নিরাপদে link rewiring। - 06 recursion থেকে: একই কাজকে "আগে বাকি লেজটা উল্টাও (recursive call), তারপর শুধু এই node-এর link ঠিক করো" — base case + এক ধাপ + বিশ্বাস যে ছোট অংশ ঠিক হয়ে এসেছে।
iterative রূপ পুরোটা 04; recursive রূপ 04-এর arrow surgery-কে 06-এর call stack-এ বসায়। দুটো লেখাই এই combo চেনার drill।
3. What is the hidden math or logic?¶
লুকানো logic একটা invariant (iterative-এ) আর একটা induction (recursive-এ):
- Iterative:
prevসবসময় এ পর্যন্ত উল্টানো অংশের head,curনা-ছোঁয়া অংশের head। প্রতি step এই invariant ধরে রাখে। - Recursive: ধরে নাও
reverse(head.next)লেজটা ঠিকঠাক উল্টে দিয়েছে; তখন শুধুhead.next.next = head(পরের node-কে নিজের দিকে ফেরাও) আরhead.next = Noneকরলেই পুরো জিনিস উল্টে যায়। এটাই mathematical induction-এর "ছোট ক্ষেত্র ঠিক ⇒ বড় ক্ষেত্র ঠিক"।
4. Which data structure is needed?¶
কোনো extra data structure লাগে না।
- Iterative: তিনটা pointer (
prev,cur,nxt) — O(1) extra space। - Recursive: কোনো explicit structure নেই, কিন্তু call stack O(n) গভীর হয় (06 recursion-এর দাম)।
5. Why this data structure fits¶
Linked list-এ node গুলো memory-তে ছড়ানো, শুধু next arrow দিয়ে বাঁধা — তাই index না করে আমরা arrow নিজে বদলাই। iterative-এ তিন pointer দিয়ে link এক এক করে নিরাপদে ঘোরে, কোনো node না হারিয়ে। recursive-এ call stack নিজেই "পরের node কোথায়" মনে রেখে দেয়, তাই আলাদা nxt লাগে না — 06 recursion-এর সাথে এই কারণে খাপ খায়।
6. Real-life analogy¶
একটা ট্রেনের বগি ভাবো, প্রতিটা সামনের বগির সাথে যুক্ত, তুমি পুরো ট্রেন উল্টো দিকে চালাতে চাও।
- Iterative = তুমি সামনে থেকে এক এক করে সংযোগ খুলে উল্টো দিকে জুড়ছ, খোলার আগে পরের বগি মনে রাখছ (
nxt)। - Recursive = তুমি ভেতরে হেঁটে গিয়ে আগে শেষ বগিতে পৌঁছাও, তারপর ফেরার পথে প্রতিটা সংযোগ পেছনে জুড়তে জুড়তে আসো — call stack-ই তোমাকে ফেরার পথ মনে রাখায়।
7. Visual explanation¶
দুই রূপ পাশাপাশি, 1 -> 2 -> 3 দিয়ে:
ITERATIVE (prev, cur, nxt):
শুরু: prev=None cur=1 -> 2 -> 3
step1: nxt=2; 1.next=None; prev=1, cur=2 => None<-1 2->3
step2: nxt=3; 2.next=1; prev=2, cur=3 => None<-1<-2 3
step3: nxt=None; 3.next=2; prev=3, cur=None=> None<-1<-2<-3
head = prev = 3
RECURSIVE (ভেতরে যাও, ফেরার পথে জোড়ো):
reverse(1) -> reverse(2) -> reverse(3) -> reverse(None) base
ফেরা: 3 head ধরে রাখলাম
2.next.next = 2 (3->2); 2.next=None
1.next.next = 1 (2->1); 1.next=None
head = 3
8. Example input and output¶
Input : 1 -> 2 -> 3 -> 4 -> 5
Output: 5 -> 4 -> 3 -> 2 -> 1
Edge case 1 (খালি list): Input: None -> Output: None
Edge case 2 (একটা node): Input: 7 -> Output: 7
9. Brute force thinking¶
প্রথম সরল চিন্তা: list ঘুরে সব value একটা array-তে রাখি, array উল্টাই, তারপর নতুন list বানাই।
10. Why brute force is slow¶
এটা কাজ করে, কিন্তু O(n) extra space নেয় (array + নতুন node)। interview-র আসল চ্যালেঞ্জ in-place উল্টানো (iterative-এ O(1) space)। তাছাড়া নতুন node বানানো অপচয় — পুরনো node-এর arrow ঘোরালেই হয় (04 pointer surgery)।
11. Key observation¶
মূল observation: পুরো list একসাথে উল্টানোর দরকার নেই — প্রতিটা node-এর next কে তার আগের node-এর দিকে ঘুরিয়ে দিলেই হয়। শুধু arrow ঘোরানোর আগে পরের node হারিয়ে না ফেলা (iterative-এ nxt, recursive-এ call stack এটা সামলায়)।
12. Optimized intuition¶
- Iterative: তিন pointer নিয়ে একবার হাঁটো — পরেরটা মনে রাখো, এখনকার arrow পিছনে ঘোরাও, দুজনে এক ঘর এগোও।
- Recursive: base case (খালি বা একক node) ফেরত দাও যেমন আছে; নাহলে আগে
reverse(head.next)দিয়ে লেজ উল্টাও, তারপরhead.next.next = headকরে নিজের পরের node-কে নিজের দিকে ফেরাও, আরhead.next = Noneদিয়ে পুরনো arrow কাটো।
13. Thinking tweak¶
মোচড়: "list উল্টাব" ভাবার বদলে ভাবো "প্রতিটা arrow আলাদা করে পিছনে ঘোরাব।" iterative-এ পরেরটা আগে ধরে রাখি; recursive-এ লেজটা আগে উল্টিয়ে নিই, তারপর শুধু একটা link ঠিক করি — call stack বাকি মনে রাখে।
14. Step-by-step dry run¶
Input 1 -> 2 -> 3 (iterative):
- শুরু:
prev=None,cur=1 nxt=2;1.next=None;prev=1,cur=2→ উল্টানো অংশ:1nxt=3;2.next=1;prev=2,cur=3→ উল্টানো অংশ:2 -> 1nxt=None;3.next=2;prev=3,cur=None→ উল্টানো অংশ:3 -> 2 -> 1cur=None→ থামো; returnprev = 3
15. Python solution¶
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverse_iterative(head):
prev = None
cur = head
while cur:
nxt = cur.next # পরেরটা আগে ধরে রাখো
cur.next = prev # arrow পিছনে ঘোরাও
prev = cur # prev এক ঘর এগোলো
cur = nxt # cur এক ঘর এগোলো
return prev # নতুন head
def reverse_recursive(head):
if head is None or head.next is None:
return head # base: খালি বা একক node নিজেই উল্টানো
new_head = reverse_recursive(head.next) # আগে লেজ উল্টাও
head.next.next = head # পরের node-কে নিজের দিকে ফেরাও
head.next = None # পুরনো arrow কাটো
return new_head # নতুন head (সবচেয়ে শেষ node) উপরে পাঠাও
# ---- helpers (test-এর জন্য) ----
def build(values):
dummy = ListNode()
tail = dummy
for v in values:
tail.next = ListNode(v)
tail = tail.next
return dummy.next
def to_list(head):
out = []
while head:
out.append(head.val)
head = head.next
return out
# ---- tests (দুই রূপই একই উত্তর দিতে হবে) ----
for reverse in (reverse_iterative, reverse_recursive):
assert to_list(reverse(build([1, 2, 3, 4, 5]))) == [5, 4, 3, 2, 1]
assert to_list(reverse(build([]))) == [] # খালি list
assert to_list(reverse(build([7]))) == [7] # একটা node
assert to_list(reverse(build([1, 2]))) == [2, 1] # দুই node
print("সব test pass!")
16. Line-by-line code explanation¶
Iterative:
prev=None,cur=head— উল্টানো অংশ খালি; না-ছোঁয়া অংশ head থেকে শুরু।nxt = cur.next— arrow ঘোরানোর আগে পরের node বাঁচাও।cur.next = prev— এই node-এর arrow পিছনে ঘোরাও।prev=cur/cur=nxt— দুজনে এক ঘর এগোয়; শেষেreturn prev।
Recursive:
if head is None or head.next is None: return head— base case: খালি বা শেষ node নিজেই নতুন head।new_head = reverse_recursive(head.next)— বিশ্বাস করো লেজটা উল্টে এসেছে;new_headপুরো উল্টানো list-এর head।head.next.next = head— এখনhead.nextহলো লেজের শেষ node; তারnext-কেhead-এর দিকে ফেরাও।head.next = None—head-এর পুরনো forward arrow কেটে দাও (নাহলে cycle হবে)।return new_head— অপরিবর্তিত head সব level-এ উপরে পাঠাও।
17. Output walkthrough¶
build([1,2,3,4,5]) দেয় 1->...->5। দুই রূপই 5->4->3->2->1 বানায়, to_list দেয় [5,4,3,2,1] — assert pass। for reverse in (...) লুপ দুই function-এই খালি/একক/জোড়া case চালায়, সবই pass। শেষে print: সব test pass!।
18. Time complexity¶
O(n) দুই রূপেই — প্রতিটা node ঠিক একবার করে visit হয় ও arrow ঘোরে।
19. Space complexity¶
- Iterative: O(1) — শুধু তিন pointer।
- Recursive: O(n) — call stack list-এর গভীরতার সমান (06 recursion-এর hidden cost; খুব লম্বা list-এ stack overflow-র ঝুঁকি, তাই iterative-টা production-safe)।
20. Common mistakes¶
- Iterative-এ
nxtরাখার আগেইcur.next = prevকরা — বাকি list হারায়। - Recursive-এ
head.next = Noneভুলে যাওয়া — পুরনো arrow থেকে যায়, ফলে শেষে cycle তৈরি হয়। - Recursive base case-এ শুধু
head is Noneদেখা (head.next is Noneবাদ) — একক node-এhead.next.nextcrash করবে। - শেষে
prev-এর বদলেcurreturn করা (iterative-এcurতখন None)।
21. Which basic problem this inherits from¶
ভিত্তি: linked list traversal + এক জোড়া node-এর arrow ঘোরানো (04-এর ../../04-linked-lists/), আর "ছোট অংশ আগে সমাধান করে, ফেরার পথে নিজের কাজ" recursion (06-এর ../../06-recursion-and-backtracking/)। topic-03-এর 001-reverse-linked-list.md-ও দেখো — এটা তার দুই-রূপ সম্প্রসারণ।
22. Similar harder problems¶
- Reverse Linked List II (শুধু একটা অংশ উল্টানো) — https://leetcode.com/problems/reverse-linked-list-ii/
- Reverse Nodes in k-Group — https://leetcode.com/problems/reverse-nodes-in-k-group/
- Palindrome Linked List (অর্ধেক উল্টানো লাগে) — https://leetcode.com/problems/palindrome-linked-list/
23. Pattern learned¶
Pointer surgery, two ways: একই reversal-কে (a) তিন-pointer iterative loop, O(1) space আর (b) "লেজ আগে, link পরে" recursive, O(n) stack — দুইভাবে লেখা। iterative ↔ recursive এই অনুবাদটা বহু list/tree problem-এ লাগে।
24. Final summary¶
ভবিষ্যতের আমাকে: list উল্টানো = প্রতিটা arrow পিছনে ঘোরানো। iterative-এ "পরেরটা আগে ধরো"; recursive-এ "লেজ আগে উল্টাও, তারপর head.next.next = head; head.next = None"। interview-তে যেকোনো একটা চাইলে অন্যটাও বলে দাও — combo চেনার এটাই flex। দুটোই চোখ বন্ধ করে লিখতে পারা চাই।
মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" দাবি নেই — এটা একটা common interview pattern।