013 — Reverse Linked List II¶
Problem link ও উৎস
- 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 সেলাই
প্রশ্ন ও উদাহরণ¶
সহজ করে প্রশ্ন¶
একটা linked list আর দুটো position left ও right (১ থেকে গোনা) দেওয়া। শুধু left থেকে right পর্যন্ত অংশটুকু উল্টে দাও, বাকিটা যেমন আছে তেমন থাকবে। এক পাসে করো। নতুন head return করো।
list : 1 -> 2 -> 3 -> 4 -> 5 , left = 2 , right = 4
শুধু 2..4 উল্টাও
ফল : 1 -> 4 -> 3 -> 2 -> 5
Input ও 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
চেনা উদাহরণে বোঝা¶
একটা বইয়ের তাকের নির্দিষ্ট কয়েকটা বই উল্টো ক্রমে সাজাতে চাও। বাঁদিকের একটা স্থির বইকে খুঁটি ধরে, তার ঠিক ডানপাশের বইটা বারবার তুলে এনে খুঁটির পরে গুঁজে দাও — অংশটুকু উল্টে যায়, আশপাশের সাজানো বই অটুট থাকে।
ভিত্তির ধারণা¶
আগের কোন ধারণাটি লাগবে¶
এর ভিত্তি problem 1 ([001-reverse-linked-list.md](001-reverse-linked-list.md))-এর full reversal। সেখানে পুরো list উল্টানো হয়; এখানে শুধু একটা অংশ। নতুন চ্যালেঞ্জ: উল্টানো অংশের দুই প্রান্ত আবার সঠিকভাবে বাকি list-এর সাথে সেলাই (stitch) করা — না হলে list ভেঙে যায়।
ভেতরের যুক্তি¶
লুকানো কৌশল head-insertion: left-এর ঠিক আগের node-কে (prev) একটা স্থির খুঁটি ধরে রাখো। তারপর অংশের ভেতরের প্রতিটা পরের node-কে এক এক করে তুলে এনে prev-এর ঠিক পরে বসাও। এতে ঠিক right − left বার এই "তুলে সামনে বসানো" করলে অংশটা উল্টে যায়, আর boundary দুটো আপনাআপনি ঠিক থাকে।
কী দিয়ে তথ্য রাখব¶
কোনো extra structure নয় — একটা dummy head (যদি left = 1 হয়ে head বদলায়) আর কয়েকটা pointer (prev, cur, nxt)। O(1) extra space।
এটি কেন উপযুক্ত¶
Linked list-এ একটা node-কে অন্য জায়গায় সরানো মানে শুধু কয়েকটা arrow বদল — O(1), data সরানো লাগে না। head-insertion ঠিক এই সুবিধাটাই খাটায়: prev খুঁটি স্থির রেখে ভেতরের node গুলো সামনে এনে বসানো। dummy head থাকায় left = 1-এর special case আলাদা সামলাতে হয় না।
ধাপে ধাপে সমাধান¶
প্রথম সরল উপায়¶
সরল চিন্তা: left..right অংশের value গুলো একটা array-তে নাও, array উল্টাও, তারপর সেই উল্টানো value গুলো আবার ওই অংশের node-এ বসিয়ে দাও।
কোথায় বেশি কাজ হচ্ছে¶
value-copy করলে O(k) extra space লাগে (k = অংশের দৈর্ঘ্য), আর অনেক ক্ষেত্রে value নয়, node উল্টানোই উদ্দেশ্য। head-insertion দিয়ে কোনো array ছাড়াই, এক পাসে, পুরোনো node গুলোর arrow ঘুরিয়েই O(1) space-এ হয়ে যায়।
যে বিষয়টি খেয়াল করতে হবে¶
মূল observation: পুরো অংশ আলাদা করে উল্টে আবার জোড়ার দরকার নেই। left-এর আগের node-কে খুঁটি ধরে, ভেতরের পরের node-টা বারবার সামনে এনে বসালেই অংশটা উল্টে যায় — এবং দুই প্রান্তের সেলাই আপনাআপনি ঠিক থাকে।
আরও কম কাজে সমাধান¶
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।
ভাবনার পরিবর্তন¶
মোচড়: "অংশটা কেটে, উল্টে, আবার জুড়ব" না ভেবে ভাবো — "একটা খুঁটির পরে ভেতরের node গুলো এক এক করে সামনে এনে বসাই; ঠিক ততবার যতটা লম্বা অংশ।" তাহলে boundary সেলাই আর আলাদা করে ভাবতে হয় না।
ছবিতে বোঝা¶
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
ছোট input-এ ধাপে ধাপে¶
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
কোড ও ব্যাখ্যা¶
Python¶
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!")
কোডের প্রতিটি অংশ¶
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।
Output মিলিয়ে দেখা¶
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!।
খরচ ও সাধারণ ভুল¶
সময়¶
O(n) — prev-এ পৌঁছাতে রৈখিক, তারপর right − left বার head-insertion; মোট list-এর উপর এক পাস।
অতিরিক্ত জায়গা¶
O(1) — শুধু dummy + কয়েকটা pointer; পুরোনো node reuse, extra memory বাড়ে না।
যে ভুলগুলো এড়াতে হবে¶
- তিনটা arrow ভুল ক্রমে সেট করা —
cur.next = nxt.nextআগে না করলে অংশের পরের node হারায়। prevকেleft − 1-এর বদলেleftঘর এগিয়ে নেওয়া — তখন উল্টানো এক ঘর সরে যায়।left = 1(head বদলায়) case-এর জন্য dummy না নেওয়া।- loop চালানো
right − left + 1বার (এক বার বেশি) — boundary ভেঙে যায়; ঠিকright − leftবার।
শেখা ও পরের অনুশীলন¶
ভিত্তির সঙ্গে সম্পর্ক¶
ভিত্তি: problem 1 ([001-reverse-linked-list.md](001-reverse-linked-list.md))-এর full reversal; তার উপর head-insertion + boundary সেলাই যোগ করে অংশ-উল্টানো। dummy head কৌশল problem 2 ([002-merge-two-sorted-lists.md](002-merge-two-sorted-lists.md)) থেকে।
পরের অনুশীলন¶
- 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)
কৌশলটি মনে রাখুন¶
Segmented reversal via head-insertion: left-এর আগের node-কে খুঁটি ধরে ভেতরের পরের node বারবার সামনে এনে বসাও; right − left বার করলে অংশ উল্টে যায়, boundary আপনাআপনি ঠিক।
নিজেকে যাচাই করুন¶
ভবিষ্যতের আমাকে: "অংশ উল্টাতে হলে — 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।