৫টি সহজ Linked List কোডিং প্রবলেম ও সমাধান (Java)
Tree ও BFS/DFS-এর পর এবার Linked List — ইন্টারভিউয়ারদের প্রিয় জায়গা, কারণ এখানে pointer manipulation-এ সামান্য অসাবধানতাই ধরা পড়ে যায়। এই ৫টি প্রবলেমে Linked List-এর ৩টি মহাপ্যাটার্ন আছে: pointer reversal, fast/slow pointer, dummy node।
সব প্রবলেমে এই ListNode ক্লাসটি ব্যবহার হবে:
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
}
প্রবলেম ১: Reverse Linked List (LeetCode 206)
প্রশ্ন: একটি singly linked list উল্টে দিন। 1→2→3→4 হয়ে যাবে 4→3→2→1।
চিন্তার ধাপ: তিনটি pointer — prev, curr, আর next। প্রতি ধাপে: next-টা আগে বাঁচিয়ে রাখুন (নইলে বাকি list হারিয়ে যাবে!), তারপর curr-এর তীরটি পেছনে ঘুরিয়ে দিন, তারপর দুজনেই এক ঘর এগোন।
সমাধান:
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next; // ১. বাকি list বাঁচিয়ে রাখুন
curr.next = prev; // ২. তীর উল্টে দিন
prev = curr; // ৩. prev এগোল
curr = next; // ৪. curr এগোল
}
return prev; // curr এখন null, prev-ই নতুন head
}
Complexity: Time O(n), Space O(1)।
ইন্টারভিউ টিপ: এটি Linked List-এর "hello world" — চোখ বন্ধ করে লিখতে পারা চাই, কারণ এটি বহু কঠিন প্রবলেমের (Reverse in k-Groups, Palindrome check) ভেতরের যন্ত্রাংশ। Follow-up: "recursive version?" — লিখতে পারা ভালো, কিন্তু বলুন recursion-এ space O(n) হয়ে যায় (call stack), তাই iterative-ই উৎকৃষ্ট।
প্রবলেম ২: Middle of the Linked List (LeetCode 876)
প্রশ্ন: List-এর মাঝের node-টি রিটার্ন করুন। জোড় সংখ্যক node হলে দ্বিতীয় মাঝেরটি।
চিন্তার ধাপ: Fast/slow pointer (কচ্ছপ-খরগোশ) — slow এক ঘর করে চলে, fast দুই ঘর। Fast শেষে পৌঁছালে slow ঠিক মাঝখানে। এক pass-এই কাজ শেষ — length গুনে দ্বিতীয়বার হাঁটার দরকার নেই।
সমাধান:
public ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next; // ১ ঘর
fast = fast.next.next; // ২ ঘর
}
return slow;
}
Complexity: Time O(n), Space O(1)।
ইন্টারভিউ টিপ: While-এর শর্তটি মুখস্থ করুন — fast != null && fast.next != null। ক্রমটি উল্টালে (আগে fast.next) জোড় দৈর্ঘ্যে NullPointerException। এই দুটি চেকের ক্রমই এই প্যাটার্নের সবচেয়ে common bug।
প্রবলেম ৩: Linked List Cycle (LeetCode 141)
প্রশ্ন: List-এ cycle আছে কিনা বের করুন (কোনো node-এর next যদি আগের কোনো node-কে দেখায়)।
চিন্তার ধাপ: আবার fast/slow — Floyd's cycle detection। Cycle থাকলে দৌড়ের ট্র্যাকের মতো: দ্রুত দৌড়বিদ ধীরটিকে একসময় ধরে ফেলবেই (প্রতি ধাপে ব্যবধান ১ করে কমে)। Cycle না থাকলে fast আগে null-এ পৌঁছে যাবে।
সমাধান:
public boolean hasCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true; // ধরা পড়েছে → cycle আছে
}
return false; // fast শেষে পৌঁছেছে → cycle নেই
}
Complexity: Time O(n), Space O(1)।
ইন্টারভিউ টিপ: সহজ বিকল্প — HashSet-এ দেখা node রাখা — সেটাও বলুন, কিন্তু উল্লেখ করুন সেটি O(n) space; Floyd O(1)। নিশ্চিত follow-up: "cycle-টি কোথায় শুরু হয়েছে?" (LeetCode 142) — উত্তর: মিলনের পর একটি pointer head-এ ফিরিয়ে দিন, দুজনেই ১ ঘর করে চলুক — যেখানে আবার মিলবে সেটিই cycle-এর শুরু। এটুকু জানা থাকলেই ওই follow-up জেতা।
প্রবলেম ৪: Merge Two Sorted Lists (LeetCode 21)
প্রশ্ন: দুটি sorted linked list-কে একটি sorted list-এ মিশিয়ে দিন।
চিন্তার ধাপ: এখানে আসে তৃতীয় মহাপ্যাটার্ন — dummy node। নতুন list-এর head কে হবে তা নিয়ে বিশেষ case লেখার বদলে একটি dummy node দিয়ে শুরু করুন; শেষে dummy.next-ই উত্তর। তারপর দুই list-এর মাথা তুলনা করে ছোটটিকে জুড়তে থাকুন — merge sort-এর merge ধাপের মতোই।
সমাধান:
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1); // head-এর special case দূর
ListNode tail = dummy;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
tail.next = l1;
l1 = l1.next;
} else {
tail.next = l2;
l2 = l2.next;
}
tail = tail.next;
}
// একটি list শেষ, অন্যটির বাকি অংশ পুরোটা জুড়ে দিন
tail.next = (l1 != null) ? l1 : l2;
return dummy.next;
}
Complexity: Time O(m+n), Space O(1) — নতুন node তৈরি হয়নি, শুধু তীর ঘোরানো হয়েছে।
ইন্টারভিউ টিপ: শেষের tail.next = (l1 != null) ? l1 : l2; লাইনটি লক্ষ করুন — বাকি অংশ node-by-node কপি করার দরকার নেই, পুরো অবশিষ্ট list এক লাইনে জুড়ে যায়। Follow-up: "k টি sorted list হলে?" (LeetCode 23) → PriorityQueue অথবা divide & conquer, O(N log k)।
প্রবলেম ৫: Remove Nth Node From End (LeetCode 19)
প্রশ্ন: List-এর শেষ থেকে n-তম node-টি মুছে দিন — এক pass-এ।
চিন্তার ধাপ: দুটি pointer-এর মাঝে n ঘরের ব্যবধান তৈরি করুন: fast-কে আগে n ধাপ এগিয়ে দিন, তারপর দুজনে একসাথে চলুক। Fast শেষে পৌঁছালে slow থাকবে ঠিক মুছে-ফেলার-node-এর আগের ঘরে। আর head নিজেই মুছে যেতে পারে বলে — dummy node।
সমাধান:
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode fast = dummy;
ListNode slow = dummy;
// fast-কে n+1 ধাপ এগিয়ে দিন (dummy থেকে), ব্যবধান তৈরি হলো
for (int i = 0; i <= n; i++) {
fast = fast.next;
}
// দুজনে একসাথে — fast null হলে slow টার্গেটের আগের node-এ
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next; // মুছে ফেলা
return dummy.next;
}
Complexity: Time O(n), Space O(1)।
ইন্টারভিউ টিপ: "কেন dummy?" — যদি [1,2,3] থেকে শেষ থেকে ৩য় (মানে head) মুছতে হয়, dummy ছাড়া আলাদা if লিখতে হতো। Head বদলে যেতে পারে এমন যেকোনো প্রবলেমে হাত অটোমেটিক dummy-তে যাওয়া চাই। আর "দুই pass-এ তো সহজ — length গুনে আবার হাঁটা" — সেটা বলে তারপর এক-pass সমাধান দিন; নিজে থেকে trade-off বলা senior signal।
৩টি মহাপ্যাটার্ন — মনে রাখুন
| প্যাটার্ন | কখন | এই তালিকায় |
|---|---|---|
| Pointer reversal (prev/curr/next নাচ) | তীর উল্টাতে হলে | প্রবলেম ১ |
| Fast/slow pointer | মাঝ খোঁজা, cycle, শেষ-থেকে-n-তম | প্রবলেম ২, ৩, ৫ |
| Dummy node | Head বদলে যেতে পারে / নতুন list বানানো | প্রবলেম ৪, ৫ |
আর দুটি সোনার নিয়ম:
১. next আগে বাঁচান, পরে তীর ঘোরান — linked list bug-এর ৮০% হলো লিঙ্ক আগেই কেটে ফেলা।
২. কাগজে ৩-৪টি node এঁকে হাতে trace করুন — linked list মাথায় নয়, কাগজে সমাধান হয়। ইন্টারভিউতেও এঁকে দেখানো ভালো দেখায়।
পরবর্তী ধাপ: এগুলোর পর medium — Reorder List (143), Add Two Numbers (2), Palindrome Linked List (234 — মাঝ খোঁজা + reverse, অর্থাৎ প্রবলেম ১ ও ২-এর সংমিশ্রণ!), LRU Cache (146 — HashMap + doubly linked list, Staff loop-এর অতি প্রিয় প্রশ্ন)।