৫টি সহজ Array কোডিং প্রবলেম ও সমাধান (Java)
সিরিজের পঞ্চম ফাইল। Array হলো ইন্টারভিউয়ের সবচেয়ে বড় ময়দান — এখানকার প্যাটার্নগুলো: HashMap দিয়ে complement খোঁজা, one-pass tracking (min/max), two pointer (in-place), Kadane's algorithm, prefix product। এই ৫টি প্রবলেম এমনভাবে বাছা যে প্রতিটি একটি করে নতুন প্যাটার্ন শেখায়।
প্রবলেম ১: Two Sum (LeetCode 1)
প্রশ্ন: একটি array ও একটি target দেওয়া। যে দুটি সংখ্যার যোগফল target, তাদের index রিটার্ন করুন। [2,7,11,15], target 9 → [0,1]।
চিন্তার ধাপ: পৃথিবীর সবচেয়ে বিখ্যাত ইন্টারভিউ প্রশ্ন। Brute force: প্রতিটি জোড়া চেক — O(n²)। চাবিকাঠি: প্রতিটি সংখ্যার জন্য প্রশ্নটা উল্টে দিন — "আমার complement (target − আমি) কি আগে দেখা গেছে?" HashMap-এ দেখা সংখ্যা ও তার index রাখুন — এক pass-এই শেষ।
সমাধান:
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> seen = new HashMap<>(); // value → index
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (seen.containsKey(complement)) {
return new int[]{seen.get(complement), i};
}
seen.put(nums[i], i); // চেকের *পরে* রাখুন
}
return new int[]{}; // প্রশ্নমতে সমাধান সবসময় আছে
}
Complexity: Time O(n), Space O(n)।
ইন্টারভিউ টিপ: seen.put(...) টি complement চেকের পরে — আগে রাখলে target = 6, nums = [3, ...]-এ 3 নিজেকেই নিজের জোড়া বানিয়ে ফেলবে। এই ক্রমটাই প্রবলেমের আসল ফাঁদ। নিশ্চিত follow-up: "array টি sorted হলে?" → তখন HashMap লাগে না — দুই প্রান্ত থেকে two pointer, O(1) space (LeetCode 167)। Sorted শুনলেই two pointer/binary search মাথায় আসা চাই।
প্রবলেম ২: Best Time to Buy and Sell Stock (LeetCode 121)
প্রশ্ন: প্রতিদিনের শেয়ারের দাম দেওয়া। একবার কিনে একবার বেচে সর্বোচ্চ লাভ কত? [7,1,5,3,6,4] → 5 (১-এ কিনে ৬-এ বেচা)। কেনা অবশ্যই বেচার আগে।
চিন্তার ধাপ: One-pass tracking — হাঁটতে হাঁটতে দুটি জিনিস মনে রাখুন: এ পর্যন্ত দেখা সর্বনিম্ন দাম, আর "আজ বেচলে লাভ কত" (আজকের দাম − সর্বনিম্ন)। প্রতিদিন সর্বোচ্চ লাভ update করুন। ভবিষ্যৎ জানার দরকার নেই — অতীতের minimum-ই যথেষ্ট।
সমাধান:
public int maxProfit(int[] prices) {
int minPrice = Integer.MAX_VALUE;
int maxProfit = 0;
for (int price : prices) {
if (price < minPrice) {
minPrice = price; // নতুন সর্বনিম্ন কেনার দাম
} else if (price - minPrice > maxProfit) {
maxProfit = price - minPrice; // আজ বেচলে বেশি লাভ?
}
}
return maxProfit;
}
Complexity: Time O(n), Space O(1)।
ইন্টারভিউ টিপ: দাম শুধু কমতেই থাকলে ([7,6,4,3,1]) উত্তর 0 — "কিনবই না" ও একটি বৈধ সিদ্ধান্ত; maxProfit = 0 দিয়ে শুরু করাতেই সেটা সামলে যায়। এটি আসলে ছদ্মবেশী Kadane's algorithm (পরের প্রবলেম) — প্রতিদিনের দামের পার্থক্যের উপর maximum subarray। এই সংযোগটি বলতে পারলে দারুণ।
প্রবলেম ৩: Maximum Subarray (LeetCode 53) — Kadane's Algorithm
প্রশ্ন: যে টানা (contiguous) subarray-র যোগফল সর্বোচ্চ, সেই যোগফলটি দিন। [-2,1,-3,4,-1,2,1,-5,4] → 6 ([4,-1,2,1])।
চিন্তার ধাপ: Kadane's algorithm — প্রতিটি ঘরে দাঁড়িয়ে একটাই সিদ্ধান্ত: "আগের subarray-র সাথে যোগ দেব, নাকি এখান থেকে নতুন শুরু করব?" আগের চলতি যোগফল ঋণাত্মক হলে সেটি বোঝা — ফেলে দিয়ে নতুন শুরু। এই এক লাইনের সিদ্ধান্তই পুরো অ্যালগরিদম।
সমাধান:
public int maxSubArray(int[] nums) {
int currentSum = nums[0];
int maxSum = nums[0];
for (int i = 1; i < nums.length; i++) {
// আগেরটা টেনে লাভ আছে? নাকি নতুন শুরু?
currentSum = Math.max(nums[i], currentSum + nums[i]);
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}
Complexity: Time O(n), Space O(1)।
ইন্টারভিউ টিপ: maxSum = 0 দিয়ে শুরু করা common ভুল — সব সংখ্যা ঋণাত্মক হলে ([-3,-1,-2] → উত্তর -1) ভুল আসবে; তাই nums[0] দিয়ে শুরু। Follow-up গুলো জেনে রাখুন: "subarray-টাও দেখান" → নতুন শুরুর index মনে রাখুন; "divide and conquer-এ করুন" → সম্ভব, O(n log n), কিন্তু Kadane-ই শ্রেষ্ঠ; "maximum product subarray হলে?" (LeetCode 152) → ঋণাত্মক × ঋণাত্মক = ধনাত্মক বলে min-ও track করতে হয়।
প্রবলেম ৪: Move Zeroes (LeetCode 283)
প্রশ্ন: Array-র সব 0 শেষে পাঠান, বাকি সংখ্যাগুলোর ক্রম ঠিক রেখে — in-place, নতুন array ছাড়া। [0,1,0,3,12] → [1,3,12,0,0]।
চিন্তার ধাপ: Two pointer (same direction / read-write pointer) — একটি pointer (write) দেখায় পরবর্তী nonzero সংখ্যাটি কোথায় বসবে; আরেকটি (read) পুরো array হাঁটে। Read যা-ই nonzero পায়, write-এর ঘরে বসিয়ে write এগিয়ে দেয়। শেষে write থেকে বাকি সব ঘরে 0।
সমাধান:
public void moveZeroes(int[] nums) {
int write = 0;
// Pass ১: সব nonzero সামনে জড়ো করুন
for (int read = 0; read < nums.length; read++) {
if (nums[read] != 0) {
nums[write] = nums[read];
write++;
}
}
// Pass ২: বাকিটা 0 দিয়ে ভরাট
while (write < nums.length) {
nums[write] = 0;
write++;
}
}
Complexity: Time O(n), Space O(1)।
ইন্টারভিউ টিপ: এই read-write pointer প্যাটার্নটিই Remove Duplicates from Sorted Array (26), Remove Element (27) — সব in-place প্রবলেমের ছাঁচ। "In-place, order ঠিক রেখে" শুনলেই হাত এই প্যাটার্নে যাওয়া চাই। Follow-up: "লেখার সংখ্যা কমান" → nonzero হলে swap(nums[write], nums[read]) — এক pass, আগে থেকে সঠিক জায়গায় থাকা ঘরে লেখাও এড়ানো যায়।
প্রবলেম ৫: Product of Array Except Self (LeetCode 238)
প্রশ্ন: প্রতিটি index-এর জন্য বাকি সব সংখ্যার গুণফল দিন — ভাগ (division) ব্যবহার না করে, O(n)-এ। [1,2,3,4] → [24,12,8,6]।
চিন্তার ধাপ: i-তম উত্তর = (i-এর বামের সবার গুণফল) × (i-এর ডানের সবার গুণফল)। তাহলে: এক pass বাম থেকে prefix product, আরেক pass ডান থেকে suffix product। কৌশল: output array-তেই prefix রাখুন, suffix একটি চলমান variable-এ — বাড়তি array লাগে না।
সমাধান:
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] result = new int[n];
// Pass ১ (বাম → ডান): result[i] = i-এর বামের সবার গুণফল
result[0] = 1;
for (int i = 1; i < n; i++) {
result[i] = result[i - 1] * nums[i - 1];
}
// Pass ২ (ডান → বাম): চলমান suffix গুণ করে দিন
int suffix = 1;
for (int i = n - 1; i >= 0; i--) {
result[i] *= suffix;
suffix *= nums[i];
}
return result;
}
Complexity: Time O(n), Space O(1) — output array space হিসেবে ধরা হয় না (প্রশ্নেই বলা)।
ইন্টারভিউ টিপ: "ভাগ দিয়ে তো সহজ — মোট গুণফল ÷ nums[i]" — বলুন, তারপর বলুন কেন নিষেধ: 0 থাকলে ভেঙে পড়ে (একটি 0 থাকলে ওই ঘর ছাড়া সব 0; দুটি 0 থাকলে সব 0 — ভাগ দিয়ে এই case সামলানো কুৎসিত)। এই prefix/suffix চিন্তাটিই বড় হয়ে prefix sum হয় — range sum query, subarray sum equals K (560) — Staff-level array প্রবলেমের মেরুদণ্ড।
Array প্যাটার্ন — মনে রাখুন
| প্যাটার্ন | কখন চিনবেন | এই তালিকায় |
|---|---|---|
| HashMap complement | "দুটি/কয়েকটি সংখ্যার যোগ/জোড়া খুঁজুন" | প্রবলেম ১ |
| One-pass min/max tracking | "সর্বোচ্চ লাভ/পার্থক্য, ক্রম বজায় রেখে" | প্রবলেম ২ |
| Kadane's | "টানা subarray-র সর্বোচ্চ যোগফল" | প্রবলেম ৩ |
| Read-write two pointer | "in-place, order ঠিক রেখে সরান/মুছুন" | প্রবলেম ৪ |
| Prefix/suffix | "প্রতিটি ঘরের জন্য বাকি সবার হিসাব" | প্রবলেম ৫ |
তিনটি সোনার নিয়ম:
১. "Sorted" শব্দটি একটি hint — sorted শুনলেই two pointer বা binary search ভাবুন; unsorted-এ HashMap।
২. Overflow-এর কথা মুখে বলুন — গুণফলের প্রবলেমে int overflow হতে পারে, "প্রয়োজনে long নেব" এক লাইনে বলা senior signal।
৩. Edge case-এর checklist: খালি array, এক element, সব ঋণাত্মক, সব একই — কোড লেখার আগে এগুলো উচ্চারণ করুন।
পরবর্তী ধাপ: medium-এ যান — 3Sum (15, Two Sum + sorting + two pointer), Container With Most Water (11), Subarray Sum Equals K (560, prefix sum + HashMap — এই ফাইলের প্রবলেম ১ ও ৫-এর মিলন), Merge Intervals (56)।