013 — Reverse Linked List II¶
Source¶
- Original source: Classic linked list exercise
- Platform: LeetCode — https://leetcode.com/problems/reverse-linked-list-ii/
- Topic: linked list
- Difficulty: Medium
- Pattern: segmented reversal (head-insertion)
- Basic idea inherited from: full reversal (problem 1) + সাবধানে boundary সেলাই
1. Problem in simple words¶
একটা linked list আর দুটো position left ও right (১ থেকে গোনা) দেওয়া। শুধু left থেকে right পর্যন্ত অংশটুকু উল্টে দাও, বাকিটা যেমন আছে তেমন থাকবে। এক পাসে করো। নতুন head return করো।
2. Which basic idea does this inherit from?¶
এর ভিত্তি problem 1 (001-reverse-linked-list.md)-এর full reversal। সেখানে পুরো list উল্টানো হয়; এখানে শুধু একটা অংশ। নতুন চ্যালেঞ্জ: উল্টানো অংশের দুই প্রান্ত আবার সঠিকভাবে বাকি list-এর সাথে সেলাই (stitch) করা — না হলে list ভেঙে যায়।
3. What is the hidden math or logic?¶
লুকানো কৌশল head-insertion: left-এর ঠিক আগের node-কে (prev) একটা স্থির খুঁটি ধরে রাখো। তারপর অংশের ভেতরের প্রতিটা পরের node-কে এক এক করে তুলে এনে prev-এর ঠিক পরে বসাও। এতে ঠিক right − left বার এই "তুলে সামনে বসানো" করলে অংশটা উল্টে যায়, আর boundary দুটো আপনাআপনি ঠিক থাকে।
4. Which data structure is needed?¶
কোনো extra structure নয় — একটা dummy head (যদি left = 1 হয়ে head বদলায়) আর কয়েকটা pointer (prev, cur, nxt)। O(1) extra space।
5. Why this data structure fits¶
Linked list-এ একটা node-কে অন্য জায়গায় সরানো মানে শুধু কয়েকটা arrow বদল — O(1), data সরানো লাগে না। head-insertion ঠিক এই সুবিধাটাই খাটায়: prev খুঁটি স্থির রেখে ভেতরের node গুলো সামনে এনে বসানো। dummy head থাকায় left = 1-এর special case আলাদা সামলাতে হয় না।
6. Real-life analogy¶
একটা বইয়ের তাকের নির্দিষ্ট কয়েকটা বই উল্টো ক্রমে সাজাতে চাও। বাঁদিকের একটা স্থির বইকে খুঁটি ধরে, তার ঠিক ডানপাশের বইটা বারবার তুলে এনে খুঁটির পরে গুঁজে দাও — অংশটুকু উল্টে যায়, আশপাশের সাজানো বই অটুট থাকে।
7. Visual explanation¶
prev খুঁটি, প্রতিবার cur-এর পরেরটা তুলে prev-এর পরে বসাও। 1->2->3->4->5, left=2, right=4:
dummy -> 1 -> 2 -> 3 -> 4 -> 5
prev = 1 (left-এর আগের node) , cur = 2
তুলে-সামনে-বসাও (right-left = 2 বার):
nxt=3: 2->4, 3->2, 1->3 → 1 -> 3 -> 2 -> 4 -> 5
nxt=4: 2->5, 4->3, 1->4 → 1 -> 4 -> 3 -> 2 -> 5
return dummy.next = 1 -> 4 -> 3 -> 2 -> 5
8. Example input and output¶
Input : 1 -> 2 -> 3 -> 4 -> 5 , left=2 , right=4 -> Output: 1 -> 4 -> 3 -> 2 -> 5
Edge 1 (পুরোটা): 1 -> 2 -> 3 -> 4 -> 5 , left=1 , right=5 -> Output: 5 -> 4 -> 3 -> 2 -> 1
Edge 2 (এক node): 1 -> 2 -> 3 , left=2 , right=2 -> Output: 1 -> 2 -> 3 (কোনো বদল নেই)
Edge 3 (head ঘিরে): 3 -> 5 , left=1 , right=2 -> Output: 5 -> 3
9. Brute force thinking¶
সরল চিন্তা: left..right অংশের value গুলো একটা array-তে নাও, array উল্টাও, তারপর সেই উল্টানো value গুলো আবার ওই অংশের node-এ বসিয়ে দাও।
10. Why brute force is slow¶
value-copy করলে O(k) extra space লাগে (k = অংশের দৈর্ঘ্য), আর অনেক ক্ষেত্রে value নয়, node উল্টানোই উদ্দেশ্য। head-insertion দিয়ে কোনো array ছাড়াই, এক পাসে, পুরোনো node গুলোর arrow ঘুরিয়েই O(1) space-এ হয়ে যায়।
11. Key observation¶
মূল observation: পুরো অংশ আলাদা করে উল্টে আবার জোড়ার দরকার নেই। left-এর আগের node-কে খুঁটি ধরে, ভেতরের পরের node-টা বারবার সামনে এনে বসালেই অংশটা উল্টে যায় — এবং দুই প্রান্তের সেলাই আপনাআপনি ঠিক থাকে।
12. Optimized intuition¶
dummy বসিয়ে prev কে left-এর ঠিক আগের node-এ নাও (left − 1 ঘর এগিয়ে)। cur = prev.next (অংশের প্রথম node, যেটা শেষে অংশের লেজ হবে)। তারপর right − left বার: cur-এর পরের node nxt-কে তুলে prev-এর পরে বসাও (cur.next = nxt.next, nxt.next = prev.next, prev.next = nxt)। শেষে dummy.next।
13. Thinking tweak¶
মোচড়: "অংশটা কেটে, উল্টে, আবার জুড়ব" না ভেবে ভাবো — "একটা খুঁটির পরে ভেতরের node গুলো এক এক করে সামনে এনে বসাই; ঠিক ততবার যতটা লম্বা অংশ।" তাহলে boundary সেলাই আর আলাদা করে ভাবতে হয় না।
14. Step-by-step dry run¶
Input 1 -> 2 -> 3 -> 4 -> 5, left = 2, right = 4:
dummy -> 1 -> 2 -> 3 -> 4 -> 5;prev = dummyleft − 1 = 1ঘর এগোও →prev = 1;cur = prev.next = 2- বার ১:
nxt = 3;cur.next = nxt.next (=4)→2->4;nxt.next = prev.next (=2)→3->2;prev.next = nxt (=3)→1->3; এখন1 -> 3 -> 2 -> 4 -> 5 - বার ২:
nxt = 4;cur.next = nxt.next (=5)→2->5;nxt.next = prev.next (=3)→4->3;prev.next = nxt (=4)→1->4; এখন1 -> 4 -> 3 -> 2 -> 5 right − left = 2বার শেষ; returndummy.next=1 -> 4 -> 3 -> 2 -> 5
15. Python solution¶
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverse_between(head, left, right):
dummy = ListNode(0, head) # left=1 হলে head বদলায়, তাই dummy
prev = dummy
for _ in range(left - 1): # prev কে left-এর ঠিক আগের node-এ নাও
prev = prev.next
cur = prev.next # অংশের প্রথম node (শেষে অংশের লেজ হবে)
for _ in range(right - left): # head-insertion ঠিক ততবার
nxt = cur.next # তুলে আনার node
cur.next = nxt.next # cur অংশের বাকি/পরের দিকে
nxt.next = prev.next # nxt অংশের বর্তমান মাথার আগে
prev.next = nxt # prev এখন nxt-কে মাথা ধরে
return dummy.next
# ---- 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_between(build([1, 2, 3, 4, 5]), 2, 4)) == [1, 4, 3, 2, 5]
assert to_list(reverse_between(build([1, 2, 3, 4, 5]), 1, 5)) == [5, 4, 3, 2, 1] # পুরোটা
assert to_list(reverse_between(build([1, 2, 3]), 2, 2)) == [1, 2, 3] # এক node
assert to_list(reverse_between(build([3, 5]), 1, 2)) == [5, 3] # head ঘিরে
print("সব test pass!")
16. Line-by-line code explanation¶
dummy = ListNode(0, head)—left = 1-এ head বদলালেও সহজে ফেরাতে।for _ in range(left - 1):—prevকেleft-এর ঠিক আগের node-এ নিয়ে যাও।cur = prev.next— উল্টানো অংশের প্রথম node; এটাই শেষে অংশের লেজ।for _ in range(right - left):— head-insertion ঠিকright − leftবার।nxt = cur.next— যে node-টা তুলে সামনে আনব।cur.next = nxt.next—curকেnxt-এর পরেরটার সাথে জোড়ো (লেজ হারায় না)।nxt.next = prev.next—nxtকে অংশের বর্তমান মাথার আগে বসাও।prev.next = nxt—prevএখন নতুন মাথাnxt-কে ধরে।return dummy.next— আসল (সম্ভবত বদলানো) head।
17. Output walkthrough¶
build([1,2,3,4,5])-এ left=2,right=4 দিলে দুই বার head-insertion-এ [1,4,3,2,5]। left=1,right=5 হলে পুরোটা উল্টে [5,4,3,2,1]। left=right=2-তে শূন্য বার, কোনো বদল নেই। head ঘিরে [3,5],1,2 → [5,3]। সব assert pass → সব test pass!।
18. Time complexity¶
O(n) — prev-এ পৌঁছাতে রৈখিক, তারপর right − left বার head-insertion; মোট list-এর উপর এক পাস।
19. Space complexity¶
O(1) — শুধু dummy + কয়েকটা pointer; পুরোনো node reuse, extra memory বাড়ে না।
20. Common mistakes¶
- তিনটা arrow ভুল ক্রমে সেট করা —
cur.next = nxt.nextআগে না করলে অংশের পরের node হারায়। prevকেleft − 1-এর বদলেleftঘর এগিয়ে নেওয়া — তখন উল্টানো এক ঘর সরে যায়।left = 1(head বদলায়) case-এর জন্য dummy না নেওয়া।- loop চালানো
right − left + 1বার (এক বার বেশি) — boundary ভেঙে যায়; ঠিকright − leftবার।
21. Which basic problem this inherits from¶
ভিত্তি: problem 1 (001-reverse-linked-list.md)-এর full reversal; তার উপর head-insertion + boundary সেলাই যোগ করে অংশ-উল্টানো। dummy head কৌশল problem 2 (002-merge-two-sorted-lists.md) থেকে।
22. Similar harder problems¶
- Reverse Linked List (পুরোটা) — https://leetcode.com/problems/reverse-linked-list/ (এই tracker-এর #1)
- Reverse Nodes in k-Group (বারবার অংশ-উল্টানো) — https://leetcode.com/problems/reverse-nodes-in-k-group/ (#20)
- Swap Nodes in Pairs (k = 2-এর rewiring) — https://leetcode.com/problems/swap-nodes-in-pairs/ (#12)
23. Pattern learned¶
Segmented reversal via head-insertion: left-এর আগের node-কে খুঁটি ধরে ভেতরের পরের node বারবার সামনে এনে বসাও; right − left বার করলে অংশ উল্টে যায়, boundary আপনাআপনি ঠিক।
24. Final summary¶
ভবিষ্যতের আমাকে: "অংশ উল্টাতে হলে — dummy নাও, prev কে left-এর আগে নাও, cur = prev.next; তারপর cur-এর পরেরটা right−left বার তুলে prev-এর পরে বসাও।" পুরো reversal-এর ছোট ভাই, শুধু খুঁটি আর গোনা ঠিক রাখো।
মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" দাবি নেই — এটা একটা common interview pattern।