001 — Reverse Linked List¶
Source¶
- Original source: Classic linked list exercise
- Platform: LeetCode — https://leetcode.com/problems/reverse-linked-list/
- Topic: linked list
- Difficulty: Easy
- Pattern: in-place reversal (pointer rewiring)
- Basic idea inherited from: insert-at-head বারবার করা; node-to-node pointer ঘোরানো
1. Problem in simple words¶
একটা singly linked list দেওয়া আছে — মানে কতগুলো node, প্রতিটা পরেরটার দিকে একটা arrow (next) দিয়ে তাকিয়ে আছে। তোমার কাজ: সব arrow উল্টে দাও, যাতে শেষ node টা প্রথম হয়ে যায় আর প্রথমটা শেষ। নতুন head return করো।
2. Which basic idea does this inherit from?¶
মূল ভিত হলো এক জোড়া node-এর মধ্যেকার arrow ঘোরানো। তুমি যদি জানো কীভাবে a -> b কে a <- b বানাতে হয়, তাহলে পুরো list টা just সেই কাজটা বারবার করা। সাথে "insert-at-head" চিন্তা — প্রতিটা node কে নতুন list-এর সামনে এনে বসানো।
3. What is the hidden math or logic?¶
লুকানো logic একটা invariant: যেকোনো মুহূর্তে prev হলো এ পর্যন্ত উল্টে ফেলা অংশের head, আর cur হলো এখনো-না-ছোঁয়া অংশের head। প্রতি step-এ আমরা একটা node কে উল্টানো অংশে যোগ করি, এই invariant ধরে রেখে। Loop শেষ হলে prev-ই পুরো উল্টানো list-এর head।
4. Which data structure is needed?¶
কোনো extra data structure লাগে না — শুধু তিনটা pointer (prev, cur, nxt)। এটাই এর সৌন্দর্য: O(1) extra space।
5. Why this data structure fits¶
Linked list-এ node গুলো memory-তে ছড়িয়ে থাকে, শুধু next arrow দিয়ে বাঁধা। তাই array-র মতো index করা যায় না, কিন্তু arrow নিজে বদলানো যায় সহজে। তিনটা pointer দিয়ে আমরা link গুলো এক এক করে নিরাপদে ঘুরিয়ে দিতে পারি — কোনো node না হারিয়ে।
6. Real-life analogy¶
একটা ট্রেনের বগি ভাবো, প্রতিটা সামনের বগির সাথে যুক্ত। তুমি চাও পুরো ট্রেন উল্টো দিকে চলুক। তুমি এক এক করে প্রতিটা সংযোগ খুলে উল্টো দিকে জুড়ছ — কিন্তু খোলার আগে পরের বগিটা কোথায় তা মনে রাখতে হবে, নাহলে বাকি ট্রেন হারিয়ে যাবে। সেই "মনে রাখা" = nxt pointer।
7. Visual explanation¶
prev, cur, nxt তিনজনের নাচ। 1 -> 2 -> 3 দিয়ে দেখি:
শুরু: prev=None cur=1 -> 2 -> 3 -> None
step 1: nxt = 2 (মনে রাখলাম)
1.next = None (arrow উল্টালো)
prev=1, cur=2
অবস্থা: None <- 1 2 -> 3 -> None
step 2: nxt = 3
2.next = 1
prev=2, cur=3
অবস্থা: None <- 1 <- 2 3 -> None
step 3: nxt = None
3.next = 2
prev=3, cur=None
অবস্থা: None <- 1 <- 2 <- 3
cur = None → থামো। নতুন head = prev = 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 (O(1) space) উল্টানো। তাছাড়া নতুন node বানানো অপচয় — পুরোনো node গুলোর arrow ঘোরালেই তো হয়।
11. Key observation¶
মূল observation: পুরো list একসাথে উল্টানোর দরকার নেই — শুধু প্রতিটা node-এর next কে তার আগের node-এর দিকে ঘুরিয়ে দিলেই হয়। শুধু খেয়াল রাখতে হবে, arrow ঘোরানোর আগে পরের node-টা হারিয়ে না ফেলি।
12. Optimized intuition¶
তিনটা pointer নিয়ে list-এর উপর একবার হাঁটো। প্রতি ধাপে: পরের node মনে রাখো (nxt), এখনকার node-এর arrow পিছনে ঘোরাও (cur.next = prev), তারপর prev আর cur কে এক ঘর এগিয়ে দাও। একবার পাস শেষ — list উল্টে গেছে।
13. Thinking tweak¶
মোচড়: "list উল্টাব" ভাবার বদলে ভাবো "প্রতিটা arrow আলাদা করে পিছনে ঘোরাব, আর হারানো ঠেকাতে পরেরটা আগে ধরে রাখব।" বড় কাজটা একটা ছোট, repeatable step-এ ভেঙে যায়।
14. Step-by-step dry run¶
Input 1 -> 2 -> 3:
- শুরু:
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→ loop শেষ। returnprev=3
15. Python solution¶
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverse_list(head):
prev = None
cur = head
while cur:
nxt = cur.next # পরেরটা আগে ধরে রাখো (নাহলে হারাবে)
cur.next = prev # arrow পিছনে ঘোরাও
prev = cur # prev এক ঘর এগোলো
cur = nxt # cur এক ঘর এগোলো
return prev # নতুন head
# ---- 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 ----
assert to_list(reverse_list(build([1, 2, 3, 4, 5]))) == [5, 4, 3, 2, 1]
assert to_list(reverse_list(build([]))) == [] # খালি list
assert to_list(reverse_list(build([7]))) == [7] # একটা node
print("সব test pass!")
16. Line-by-line code explanation¶
prev = None— শুরুতে উল্টানো অংশ খালি, তাই এর head None।cur = head— না-ছোঁয়া অংশ শুরু হয় আসল head থেকে।while cur:— যতক্ষণ ছোঁয়ার মতো node আছে।nxt = cur.next— arrow ঘোরানোর আগে পরের node বাঁচিয়ে রাখো।cur.next = prev— এই node-এর arrow পিছনে (উল্টানো অংশের দিকে) ঘোরাও।prev = cur/cur = nxt— দুজনেই এক ঘর সামনে।return prev— loop শেষেprevপুরো উল্টানো list-এর head।
17. Output walkthrough¶
build([1,2,3,4,5]) বানায় 1->2->3->4->5। reverse_list সেটাকে 5->4->3->2->1 করে, to_list সেটাকে [5,4,3,2,1] বানায় — assert pass। খালি ও একক-node case-ও pass। শেষে print: সব test pass!।
18. Time complexity¶
O(n) — প্রতিটা node ঠিক একবার করে visit হয়, একবার করে arrow ঘোরে।
19. Space complexity¶
O(1) — শুধু তিনটা pointer; list-এর সাইজের সাথে extra memory বাড়ে না।
20. Common mistakes¶
nxt = cur.nextরাখার আগেইcur.next = prevকরে ফেলা — তখন list-এর বাকি অংশ হারিয়ে যায়।- শেষে
prev-এর বদলেcurreturn করা (curতখন None)। - খালি list (
head = None) আলাদা করে না ভাবা — যদিও এই code-এ loop চলেই না, তাই এমনিতেই ঠিক থাকে।
21. Which basic problem this inherits from¶
ভিত্তি: linked list traversal (একবার হেঁটে যাওয়া) + এক জোড়া node-এর arrow ঘোরানো। topic-এর ../implementation.py-তে reverse method আর ../visual-explanation.md-তে "reversal dance" frame দেখো।
22. Similar harder problems¶
- Reverse Linked List II (শুধু একটা অংশ উল্টানো) — https://leetcode.com/problems/reverse-linked-list-ii/ (এই tracker-এর #13)
- Reverse Nodes in k-Group — https://leetcode.com/problems/reverse-nodes-in-k-group/ (#20)
- Palindrome Linked List (অর্ধেক উল্টানো লাগে) — https://leetcode.com/problems/palindrome-linked-list/ (#14)
23. Pattern learned¶
In-place pointer reversal: তিনটা pointer (prev, cur, nxt) নিয়ে এক পাসে link ঘোরানো — linked list-এর সবচেয়ে মৌলিক pattern, বহু কঠিন problem-এর building block।
24. Final summary¶
ভবিষ্যতের আমাকে: "list উল্টানো = প্রতিটা arrow পিছনে ঘোরানো, পরেরটা আগে ধরে রেখে।" যখনই দেখবে কোনো problem-এ list/অংশ উল্টাতে হচ্ছে, এই তিন-pointer dance মনে করবে — এটা চোখ বন্ধ করে লিখতে পারা চাই।
25. JavaScript solution (with test cases)¶
const assert = (cond, msg = "") => { if (!cond) throw new Error("FAIL " + msg); };
class ListNode {
constructor(val = 0, next = null) { this.val = val; this.next = next; }
}
// ---- array <-> list helper (test চালাতে) ----
const arrayToList = (arr) => {
const dummy = new ListNode();
let tail = dummy;
for (const v of arr) { tail.next = new ListNode(v); tail = tail.next; }
return dummy.next;
};
const listToArray = (head) => {
const out = [];
while (head) { out.push(head.val); head = head.next; }
return out;
};
function reverseList(head) {
let prev = null, cur = head;
while (cur) {
const nxt = cur.next; // পরেরটা আগে ধরে রাখো (নাহলে হারাবে)
cur.next = prev; // arrow পিছনে ঘোরাও
prev = cur; // prev এক ঘর এগোলো
cur = nxt; // cur এক ঘর এগোলো
}
return prev; // নতুন head
}
// ---- test cases ----
const eq = (a, b) => JSON.stringify(a) === JSON.stringify(b);
assert(eq(listToArray(reverseList(arrayToList([1, 2, 3, 4, 5]))), [5, 4, 3, 2, 1]), "main");
assert(eq(listToArray(reverseList(arrayToList([]))), []), "খালি list");
assert(eq(listToArray(reverseList(arrayToList([7]))), [7]), "একটা node");
console.log("সব JS test pass!");
JS টীকা: linked list-এর কোনো built-in নেই, তাই ছোট একটা
ListNodeclass আরarrayToList/listToArrayconverter দিয়ে test চালাই। মূল trick একই — তিনটা pointer (prev,cur,nxt), আরconst nxt = cur.nextরাখতেই হবেcur.next = prevকরার আগে, নাহলে বাকি list হারিয়ে যায়।letদিয়ে reassignable pointer,const nxtদিয়ে প্রতি iteration-এর temporary।
26. TypeScript solution (with test cases)¶
interface ListNode { val: number; next: ListNode | null }
const node = (val: number, next: ListNode | null = null): ListNode => ({ val, next });
const arrayToList = (arr: number[]): ListNode | null => {
let head: ListNode | null = null;
for (let i = arr.length - 1; i >= 0; i--) head = node(arr[i], head);
return head;
};
const listToArray = (head: ListNode | null): number[] => {
const out: number[] = [];
while (head) { out.push(head.val); head = head.next; }
return out;
};
function reverseList(head: ListNode | null): ListNode | null {
let prev: ListNode | null = null;
let cur: ListNode | null = head;
while (cur) {
const nxt: ListNode | null = cur.next;
cur.next = prev;
prev = cur;
cur = nxt;
}
return prev;
}
function expect<T>(a: T, e: T, m = ""): void { if (a !== e) throw new Error(`FAIL ${m}`); }
const S = JSON.stringify;
expect(S(listToArray(reverseList(arrayToList([1, 2, 3, 4, 5])))), S([5, 4, 3, 2, 1]), "main");
expect(S(listToArray(reverseList(arrayToList([])))), S([]), "empty");
expect(S(listToArray(reverseList(arrayToList([7])))), S([7]), "single");
console.log("সব TS test pass!");
TS টীকা:
interface ListNode { val: number; next: ListNode | null }দিয়ে node-এর আকৃতি বাঁধা —nextহয় আরেকটা node, নয়null।curওprev-কেListNode | nullটাইপ করায় কম্পাইলার জোর করেwhile (cur)null-check করায়, তাইcur.next-এ কখনো null-dereference হবে না। এই union type-ই linked-list bug-এর সবচেয়ে বড় রক্ষাকবচ।
27. কোথায় কাজে লাগে (real-world use)¶
- Undo stack reversal: কোনো action-history list-কে উল্টো ক্রমে replay করতে in-place reversal কাজে লাগে।
- Data packet/stream reordering: networking-এ buffered chunk-গুলোকে উল্টো ক্রমে process করার দরকার হলে।
- Memory-constrained devices: embedded system-এ O(1) extra space-এ list উল্টানো জরুরি, কারণ extra array-র জায়গা নেই।
- Bigger algorithms-এর building block: reverse-in-k-group, palindrome-check, reorder-list — সবেতেই এই তিন-pointer reversal core step।
- Iterator/cursor rewind: singly-linked structure-এ পিছন দিকে হাঁটার দরকার হলে আগে অংশটা উল্টে নেওয়া হয়।
এক কথায়, যেখানেই pointer-based chain উল্টে দিতে হয় বা একটা list-কে উল্টো ক্রমে দরকার, এই O(1)-space in-place reversal-ই মৌলিক হাতিয়ার।
মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" দাবি নেই — এটা একটা common interview pattern।