সরাসরি পর্বে যান
পর্ব ০১ · ৭২-এর মধ্যে · মডিউল ১

DSA কেন শিখবেন — প্রতিটি প্রোগ্রামের পেছনের শক্তি

Why Learn Data Structures & Algorithms — The Power Behind Every Program

Taught in Bangla, this lesson explains what data structures and algorithms are and why they matter, comparing linear search, binary search and a hash table on a million contacts. It shows DSA in everyday software, gives a first look at Big-O, explains why the course uses C++ and Python, lays out the nine-module roadmap and describes how to practise with hand tracing and online judges.

এই পর্বে যা শিখবেন

  • Data structure আর algorithm কী, আর দুটির সম্পর্ক কোথায়
  • একই সমস্যার তিনটি সমাধান — linear search, binary search আর hash table — কেন এত আলাদা গতির
  • বাস্তব সফটওয়্যারে (ম্যাপ, মোবাইল ব্যাংকিং, সোশ্যাল মিডিয়া, ডেটাবেস) DSA কোথায় কাজ করে
  • পুরো কোর্সের ৯টি মডিউলের রোডম্যাপ, আর C++ ও Python কেন
  • কীভাবে অনুশীলন করবেন — হাতে ট্রেস, brute force যাচাই, online judge আর কনটেস্ট

১ · স্বাগতম — এই কোর্স কার জন্য, কীভাবে সাজানো

আপনি হয়তো একটি প্রোগ্রামিং ভাষা শিখে ফেলেছেন — লুপ, ফাংশন, অ্যারে সব চেনেন। ছোট ছোট প্রোগ্রাম লিখতে পারেন। তারপর একদিন একটি সমস্যা সামনে এলো, যেখানে আপনার প্রোগ্রাম ঠিক উত্তর দেয়, কিন্তু বড় ইনপুটে মিনিটের পর মিনিট চলতেই থাকে। অথবা ইন্টারভিউতে প্রশ্ন এলো, “আপনার সমাধানের time complexity কত?” — আর আপনি থমকে গেলেন। এই কোর্স ঠিক সেই জায়গা থেকে শুরু করে।

ডেটা স্ট্রাকচার ও অ্যালগরিদম — সংক্ষেপে DSA — হলো প্রোগ্রামিংয়ের সেই অংশ, যা “কোড লিখতে পারা” থেকে “ভালো সমাধান বানাতে পারা”-য় নিয়ে যায়। ভাষা শেখা মানে বাক্য গঠন শেখা; DSA শেখা মানে কী বলবেন আর কীভাবে গুছিয়ে বলবেন, সেটি শেখা।

কোর্সটি কাদের জন্য

নতুন শিক্ষার্থী

যিনি সবে প্রথম প্রোগ্রামিং ভাষা শেষ করেছেন। প্রতিটি ধারণা শুরু হবে দৈনন্দিন উদাহরণ আর ছবি দিয়ে, তারপর কোড।

লুপ, অ্যারে, ফাংশন জানলেই যথেষ্ট

CSE-র শিক্ষার্থী

বিশ্ববিদ্যালয়ের Data Structures আর Design & Analysis of Algorithms — দুটি কোর্সের সিলেবাসই এখানে আছে, প্রমাণ আর বিশ্লেষণসহ।

loop invariant, recurrence, NP-completeness

চাকরি ও ইন্টারভিউ

সফটওয়্যার কোম্পানির কোডিং ইন্টারভিউ, সরকারি বা ব্যাংকের প্রোগ্রামার পদের লিখিত পরীক্ষা আর ভাইভা — শেষ মডিউলে আলাদা প্রস্তুতি।

প্যাটার্ন, MCQ, লিখিত প্রশ্ন

কনটেস্ট প্রোগ্রামার

ICPC, NCPC, IUPC বা Codeforces-এর প্রস্তুতি নিচ্ছেন? segment tree, flow, string অ্যালগরিদম আর কনটেস্টের কৌশল সবই আছে।

“আরও গভীরে” বক্সে বাড়তি কৌশল

প্রতিটি পর্ব কীভাবে সাজানো

এই কোর্সে মোট ৭২টি পর্ব, ৯টি মডিউলে ভাগ করা। প্রতিটি পর্বে একই ছন্দ পাবেন, যাতে নতুন পর্বে ঢুকেই বুঝতে পারেন কোথায় কী আছে:

  • আগে ধারণা: বাজারের সারি, ট্রেনের টিকিটের লাইন, লাইব্রেরির তাক বা ফোনবুকের মতো চেনা উদাহরণ দিয়ে শুরু।
  • ধাপে ধাপে ডায়াগ্রাম (stepper): “পরে” বোতাম চেপে চেপে অ্যালগরিদমকে ছোট একটি ইনপুটে চলতে দেখবেন — প্রতিটি ধাপে অ্যারে, pointer বা টেবিলের অবস্থা বদলাবে। এগুলোই এই কোর্সের প্রাণ।
  • C++ আর Python ট্যাব: মূল অ্যালগরিদমের কোড দুই ভাষায় পাশাপাশি; আপনার ভাষার ট্যাবটি বেছে নিন। “Run” চাপলে পাতাতেই চলবে।
  • ভিজুয়ালাইজার: কোনো কোনো পর্বে ব্রাউজারে চলা ছোট টুল — স্লাইডার টেনে নিজে পরীক্ষা করবেন।
  • বিশ্লেষণ ও প্রমাণ: সময় আর মেমরির হিসাব, আর যেখানে দরকার সেখানে কেন অ্যালগরিদমটি ঠিক — তার যুক্তি।
  • “আরও গভীরে” আর “পরীক্ষা ও ইন্টারভিউতে” বক্স: প্রথমটি অভিজ্ঞদের জন্য বাড়তি তত্ত্ব; বাদ দিলেও কিছু হারাবেন না। দ্বিতীয়টিতে সাধারণ প্রশ্ন আর এক লাইনের উত্তর।
  • শেষে: ভাবনার প্রশ্ন, হাতে-কলমে অনুশীলন আর নিজেকে যাচাইয়ের কুইজ।

শুরুর আগে যা লাগবে: যেকোনো একটি ভাষায় (C, C++, Python, Java বা JavaScript) লুপ, শর্ত, অ্যারে আর ফাংশন লিখতে পারা। গণিতে স্কুল পর্যায়ের বীজগণিতই যথেষ্ট — log, যোগফলের সূত্র আর ধারা যা লাগবে, পর্ব ৬-এ শুরু থেকে শিখিয়ে নেওয়া হবে।

২ · Data structure আর algorithm — কোনটা কী

একটি লাইব্রেরির কথা ভাবুন। হাজার হাজার বই। লাইব্রেরিয়ান বইগুলো কীভাবে রাখবেন — বিষয় অনুযায়ী তাকে, লেখকের নামের বর্ণানুক্রমে, নাকি যে যেখানে পারে রেখে দেবে? এই সাজিয়ে রাখার নিয়মটিই হলো data structure. আর একজন পাঠক যখন একটি বই চান, লাইব্রেরিয়ান যে পদ্ধতিতে খোঁজেন — প্রথম তাক থেকে একটি একটি করে দেখা, নাকি সরাসরি সঠিক তাকে গিয়ে মাঝখান থেকে খোঁজা — সেটি হলো algorithm.

মূল ধারণা

Data structure = ডেটা মেমরিতে কীভাবে সাজানো থাকবে আর তার ওপর কোন কাজগুলো (যোগ করা, খোঁজা, মুছে ফেলা) করা যাবে। উদাহরণ: array, linked list, stack, queue, tree, hash table, graph.

Algorithm = একটি সমস্যা সমাধানের সুনির্দিষ্ট, ধাপে ধাপে পদ্ধতি — যা নির্দিষ্ট ইনপুট নিয়ে সীমিত সংখ্যক ধাপে সঠিক আউটপুট দেয়। উদাহরণ: binary search, merge sort, Dijkstra-র shortest path.

দুটি আলাদা হলেও একটি আরেকটিকে ছাড়া অচল। বই এলোমেলো রাখলে সবচেয়ে বুদ্ধিমান খোঁজার পদ্ধতিও কাজ করবে না — একটি একটি করে সব দেখতেই হবে। আবার বই সুন্দর করে বর্ণানুক্রমে সাজানো থাকলেও খোঁজার সময় যদি প্রথম থেকে একটি একটি দেখেন, সাজানোর পরিশ্রম বৃথা। ভালো structure ভালো algorithm-এর সুযোগ তৈরি করে, আর ভালো algorithm সেই সুযোগ কাজে লাগায়।

১৯৭৬ সালে সুইস কম্পিউটার বিজ্ঞানী Niklaus Wirth — Pascal ভাষার স্রষ্টা — একটি বই প্রকাশ করেন, যার নামটিই একটি সমীকরণ: “Algorithms + Data Structures = Programs”. তাঁর বক্তব্য ছিল, প্রোগ্রাম মানে শুধু কোডের লাইন নয়; প্রোগ্রাম হলো ডেটাকে কীভাবে সাজাবেন আর সেই সাজানো ডেটার ওপর কোন ধাপগুলো চালাবেন — এই দুটি সিদ্ধান্তের যোগফল। ১৯৮৪ সালে তিনি কম্পিউটার বিজ্ঞানের সর্বোচ্চ সম্মান Turing Award পান। আর “algorithm” শব্দটি এসেছে নবম শতকের বাগদাদের গণিতবিদ আল-খোয়ারিজমি-র নামের লাতিন রূপ থেকে, যাঁর বইয়ের মাধ্যমে দশমিক পদ্ধতিতে হিসাবের নিয়ম ইউরোপে ছড়ায়।

আরেকটি কথা আগেই পরিষ্কার করে রাখি। Data structure-এর দুটি দিক আছে: কী কী কাজ করা যায় (যেমন “একটি নাম যোগ করো”, “নামটি আছে কি না বলো”) আর ভেতরে কীভাবে বানানো। প্রথমটিকে বলে abstract data type (ADT) — যেন রেস্তোরাঁর মেনু; দ্বিতীয়টি রান্নাঘরের আসল রেসিপি। একই মেনু (“নাম খোঁজো”) সাজানো array দিয়েও বানানো যায়, hash table দিয়েও। কোনটি বেছে নেবেন, তার ওপর নির্ভর করে কোন কাজ কত দ্রুত হবে — আর এই বাছাইয়ের দক্ষতাই এই কোর্সের কেন্দ্র।

৩ · একটি প্রশ্ন, তিনটি উত্তর — ১০ লাখ কন্ট্যাক্টে একটি নম্বর

ধরুন আপনি একটি মোবাইল অপারেটরের কাস্টমার কেয়ারের সফটওয়্যার বানাচ্ছেন। ডেটাবেসে ১০ লাখ গ্রাহকের নাম আর ফোন নম্বর। প্রতি সেকেন্ডে শত শত কল আসে, আর প্রতিটি কলে সফটওয়্যারকে একটি নম্বর খুঁজে বের করতে হয়। প্রশ্নটা সরল: “এই নম্বরটি তালিকায় আছে কি না, থাকলে কোথায়?” কিন্তু কীভাবে খুঁজবেন, তার ওপর নির্ভর করছে গ্রাহক এক মুহূর্ত অপেক্ষা করবেন, নাকি লাইনে বসে থাকবেন।

বোঝার সুবিধার জন্য প্রথমে ছোট করে দেখি — মাত্র ৮টি নাম, বর্ণানুক্রমে সাজানো। আমরা খুঁজব Nadia-কে। তিনটি পদ্ধতি একে একে দেখুন।

উত্তর ১ — একটি একটি করে দেখা (linear search)

সবচেয়ে সরল চিন্তা: প্রথম নাম থেকে শুরু করে একটি একটি মিলিয়ে দেখা, যতক্ষণ না পাওয়া যায়। এর নাম linear search. বাজারের লম্বা সারিতে একজন পরিচিত মানুষকে খুঁজতে যেমন সামনে থেকে প্রত্যেকের মুখ দেখে দেখে এগোন।

1for (int i = 0; i < n; i++)2    if (name[i] == target)3        return i;          // found4return -1;                 // not found
ভ্যারিয়েবলের মান
target
i
name[i]
তুলনা
  1. শুরু। তালিকায় n = 8টি নাম, index 0 থেকে 7. খুঁজছি Nadia. এখনো কোনো তুলনা হয়নি।
  2. i = 0. Arif কি Nadia? না। প্রথম তুলনা শেষ, পরের ঘরে যাই।
  3. i = 1. Bithi কি Nadia? না। তুলনা হলো ২টি।
  4. i = 5. এর মধ্যে Chandan, Dipa, Farhan আর Mitu — আরও চারটি নাম দেখা হলো, কোনোটিই মেলেনি। মোট তুলনা ৬টি। খেয়াল করুন, তালিকা যে সাজানো, linear search সেই তথ্যটি একটুও কাজে লাগাচ্ছে না।
  5. i = 6 — পাওয়া গেছে! Nadia মিলে গেল, ফেরত দেওয়া হলো index 6. মোট ৭টি তুলনা লাগল।
  6. সবচেয়ে খারাপ ক্ষেত্র। এবার Zara খুঁজি, যিনি তালিকায় নেই। “নেই” নিশ্চিত হতে ৮টি নামের প্রত্যেকটি দেখতে হলো — ৮টি তুলনা, তারপর −1. সাধারণভাবে n টি নামে সবচেয়ে খারাপে n টি তুলনা। ১০ লাখ নামে ১০ লাখ।

উত্তর ২ — মাঝখান থেকে অর্ধেক বাদ (binary search)

অভিধানে “নদী” শব্দটি খুঁজতে কেউ প্রথম পাতা থেকে শুরু করেন না। মাঝামাঝি একটি পাতা খোলেন; সেখানে “ত” দিয়ে শব্দ থাকলে বোঝেন “ন” আরও পরে — সামনের অর্ধেক আর দেখার দরকারই নেই। তালিকা সাজানো থাকলে এই কৌশলটিই binary search: প্রতিবার মাঝের নামটির সাথে তুলনা করে বাকি অংশের অর্ধেক বাদ দেওয়া।

1int lo = 0, hi = n - 1;2while (lo <= hi) {3    int mid = lo + (hi - lo) / 2;4    if (name[mid] == target) return mid;5    if (name[mid] < target) lo = mid + 1;6    else hi = mid - 1;7}8return -1;
ভ্যারিয়েবলের মান
target
lo
hi
mid
name[mid]
তুলনা
  1. শুরু। পুরো তালিকাই সম্ভাব্য জায়গা: lo = 0, hi = 7. আটটি নামের যেকোনোটি Nadia হতে পারে।
  2. প্রথম তুলনা। mid = 0 + (7 − 0) / 2 = 3, সেখানে Dipa. বর্ণানুক্রমে Dipa আগে আসে, Nadia পরে — তাই Nadia থাকলে ডান দিকেই থাকবে। lo হলো 4. একটি তুলনায় চারটি নাম বাদ!
  3. দ্বিতীয় তুলনা। mid = 4 + (7 − 4) / 2 = 5, সেখানে Mitu. Mitu-ও Nadia-র আগে। lo হলো 6. বাকি মাত্র ২টি নাম।
  4. তৃতীয় তুলনা — পাওয়া গেছে। mid = 6 + (7 − 6) / 2 = 6, সেখানে Nadia. linear search-এ লেগেছিল ৭টি তুলনা, এখানে ৩টি।
  5. যিনি নেই, তাঁকে খুঁজলে। Zara খুঁজলে mid যায় 3 → 5 → 6 → 7; প্রতিবার নামটি Zara-র আগে, তাই lo বাড়তে বাড়তে 8 হয়, আর lo > hi হয়ে লুপ থামে। ৪টি তুলনায় নিশ্চিত — নেই। linear search-এ লেগেছিল ৮টি।
  6. বড় ছবিটা। প্রতি তুলনায় সম্ভাব্য অংশ অর্ধেক: 8 → 4 → 2 → 1. তাই n টি নামে সবচেয়ে বেশি ⌊log₂ n⌋ + 1 টি তুলনা। ১০ লাখে মাত্র ২০টি, কারণ ২²⁰ = ১০,৪৮,৫৭৬. দাম একটাই: তালিকা আগে থেকে সাজানো থাকতে হবে।

উত্তর ৩ — সরাসরি ঠিকানায় যাওয়া (hash table)

পুরোনো দিনের মোটা ফোনবুকের পাশে A, B, C… লেখা ট্যাব থাকত। “Nadia” খুঁজতে সরাসরি N-এর ট্যাবে চলে যেতেন — বাকি বইয়ের দিকে তাকানোরই দরকার পড়ত না। Hash table এই চিন্তারই কম্পিউটার রূপ। একটি hash function প্রতিটি নাম থেকে একটি সংখ্যা বানায় — সেই সংখ্যাটিই নামটির “তাক” বা bucket. খোঁজার সময় একই হিসাব করে সরাসরি সেই তাকে চলে যান।

নিচের উদাহরণে খেলনা-ধরনের একটি hash function নিচ্ছি: নামের প্রথম অক্ষরের ক্রম (A = 1, B = 2, … Z = 26) কে 5 দিয়ে ভাগ করে ভাগশেষ। অর্থাৎ ৫টি তাক, নম্বর 0 থেকে 4.

৫টি bucket (তাক) আর প্রতিটির ভেতরের নাম
hash হিসাব
bucket 0
bucket 1
bucket 2
bucket 3
bucket 4
তুলনা
  1. নিয়ম ঠিক করা। h(name) = নামের প্রথম অক্ষরের ক্রম mod 5. যেমন Arif: A = 1, তাই bucket 1; Farhan: F = 6, 6 mod 5 = 1 — সেও bucket 1. এখনো সব তাক খালি।
  2. সব নাম বসানো। প্রতিটি নামের hash হিসাব করে তার তাকে রাখা হলো। Chandan (C = 3), Mitu (M = 13) আর Rakib (R = 18) — তিনজনই bucket 3-এ। একই তাকে একাধিক নাম পড়াকে বলে collision.
  3. Nadia খোঁজা — ঠিকানা হিসাব। N হলো ১৪তম অক্ষর, 14 mod 5 = 4. অর্থাৎ Nadia থাকলে bucket 4-এই থাকবে। বাকি চারটি তাক ছুঁয়েও দেখতে হবে না।
  4. Bucket 4-এর ভেতরে। সেখানে দুটি নাম: Dipa (মেলেনি), Nadia (মিলেছে)। মোট ২টি তুলনা। তালিকা ১০ লাখ হলেও, তাকের সংখ্যা যথেষ্ট আর hash function ভালো হলে প্রতিটি তাকে গড়ে মাত্র এক-দুটি নাম থাকে — তাই খোঁজার গড় খরচ ধ্রুবক, অর্থাৎ O(1).
  5. Zara খোঁজা। Z = 26, 26 mod 5 = 1. Bucket 1-এ Arif আর Farhan — কেউই Zara নন। ২টি তুলনায় নিশ্চিত: নেই। সাজানো তালিকাও লাগেনি।
  6. দুর্বল hash-এর বিপদ। ধরুন সব গ্রাহকের নাম M দিয়ে শুরু — Mahin, Mitu, Munna, Mostafa… তখন সবাই bucket 3-এ, আর খোঁজা আবার linear search-এর মতো: সবচেয়ে খারাপে n টি তুলনা। আমাদের দেশে বহু নামের শুরুতে “Md.” থাকে, তাই “প্রথম অক্ষর” এখানে খুব খারাপ hash. আসল hash function নামের সব অক্ষর মিশিয়ে এমন সংখ্যা বানায়, যাতে নামগুলো তাকে তাকে সমানভাবে ছড়িয়ে পড়ে — পর্ব ৩২-এ বিস্তারিত।

তিন উত্তর পাশাপাশি

পদ্ধতিশর্ত / প্রস্তুতিএকবার খোঁজার খরচ (১০ লাখে)Complexity
Linear searchকিছুই লাগে নাসবচেয়ে খারাপে ১০,০০,০০০ তুলনাO(n)
Binary searchতালিকা সাজানো থাকতে হবে (সাজাতে একবার O(n log n))সবচেয়ে খারাপে ২০টি তুলনাO(log n)
Hash tableআগে table বানাতে হবে, বাড়তি মেমরি লাগেগড়ে ১-২টি তুলনাগড়ে O(1), সবচেয়ে খারাপে O(n)

তাহলে কি hash table সবসময় সেরা? না। Binary search-এর সাজানো তালিকা দিয়ে “Nadia-র পরের নাম কে?” বা “M থেকে R পর্যন্ত সব নাম দাও” — এমন প্রশ্নের উত্তর সহজেই দেওয়া যায়; hash table-এ নামগুলো এলোমেলো তাকে ছড়ানো, তাই এসব প্রশ্নে সে অসহায়। Linear search-এরও জায়গা আছে: তালিকা ছোট হলে বা মাত্র একবার খুঁজতে হলে সাজানো বা table বানানোর খরচই বেশি পড়ে। সেরা data structure বলে কিছু নেই — আছে কাজ অনুযায়ী সঠিক data structure. এই সিদ্ধান্ত নিতে পারাই DSA শেখার আসল লক্ষ্য।

৪ · নিজে চালিয়ে দেখুন — ১০ লাখ নম্বরে ধাপ গোনা

এবার আসল আকারে পরীক্ষা। নিচের প্রোগ্রাম ১০ লাখ ফোন নম্বরের একটি সাজানো তালিকা বানায় (01711000000 থেকে শুরু করে ৭ করে বেড়ে চলা নম্বর)। তারপর একই নম্বরগুলো দিয়ে একটি hash table বানায়। এরপর চারটি নম্বর খোঁজে — তালিকার প্রথম, মাঝের, শেষের, আর একটি যা তালিকায় নেই — এবং তিন পদ্ধতিতে কতগুলো তুলনা লাগল, গুনে দেখায়। আপনার ভাষার ট্যাব বেছে নিয়ে “Run” চাপুন।

Hash table-টি এখানে একটু ভিন্ন কৌশলে বানানো: প্রতিটি তাকে একটিই নম্বর থাকে, আর তাক ভরা থাকলে পরের খালি তাকে বসানো হয় — একে বলে linear probing. খোঁজার সময়ও একই পথে এগোনো হয়, যতক্ষণ না নম্বরটি বা একটি খালি তাক পাওয়া যায়।

#include <print>
#include <vector>

int main() {
    const int n = 1'000'000;
    std::vector<long long> phone(n);                  // sorted contact list
    for (int i = 0; i < n; ++i) phone[i] = 1711000000LL + 7LL * i;

    const int M = 1 << 21;                            // hash table, under half full
    std::vector<long long> table(M, -1);
    for (long long p : phone) {
        int h = p % M;
        while (table[h] != -1) h = (h + 1) % M;       // linear probing
        table[h] = p;
    }
    std::println("contacts: {}", n);
    std::println("{:<12} {:>8} {:>7} {:>5}", "number", "linear", "binary", "hash");
    for (long long t : {phone[0], phone[n / 2], phone[n - 1], 1711000003LL}) {
        int lin = 0;
        for (long long p : phone) { ++lin; if (p == t) break; }
        int bin = 0, lo = 0, hi = n - 1;
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            ++bin;
            if (phone[mid] == t) break;
            if (phone[mid] < t) lo = mid + 1; else hi = mid - 1;
        }
        int hs = 0;
        for (int h = t % M; ; h = (h + 1) % M) {
            ++hs;
            if (table[h] == t || table[h] == -1) break;
        }
        std::println("0{:<11} {:>8} {:>7} {:>5}", t, lin, bin, hs);
    }
}

আউটপুট (C++):

contacts: 1000000
number         linear  binary  hash
01711000000         1      19     1
01714500000    500001      19     1
01717999993   1000000      20     1
01711000003   1000000      20     1
n = 1_000_000
phone = [1711000000 + 7 * i for i in range(n)]        # sorted contact list

M = 1 << 21                                           # hash table, under half full
table = [-1] * M
for p in phone:
    h = p % M
    while table[h] != -1:
        h = (h + 1) % M                               # linear probing
    table[h] = p

print("contacts:", n)
print(f"{'number':<12} {'linear':>8} {'binary':>7} {'hash':>5}")
for t in (phone[0], phone[n // 2], phone[n - 1], 1711000003):
    lin = 0
    for p in phone:                                   # linear scan
        lin += 1
        if p == t: break
    bin_, lo, hi = 0, 0, n - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        bin_ += 1
        if phone[mid] == t:
            break
        if phone[mid] < t:
            lo = mid + 1
        else:
            hi = mid - 1
    hs, h = 0, t % M
    while True:
        hs += 1
        if table[h] == t or table[h] == -1:
            break
        h = (h + 1) % M
    print(f"0{t:<11} {lin:>8} {bin_:>7} {hs:>5}")

আউটপুট (Python) — C++-এর সাথে হুবহু এক:

contacts: 1000000
number         linear  binary  hash
01711000000         1      19     1
01714500000    500001      19     1
01717999993   1000000      20     1
01711000003   1000000      20     1

ফলাফল পড়ুন। প্রথম নম্বরটি linear search-এ ১টি তুলনাতেই পাওয়া গেল — এটি তার সবচেয়ে ভালো ক্ষেত্র। মাঝের নম্বরে লাগল ৫,০০,০০১টি, শেষেরটিতে আর যে নম্বর নেই তাতে পুরো ১০,০০,০০০টি। অথচ binary search চারটি ক্ষেত্রেই ১৯ বা ২০টি তুলনায় শেষ — ঠিক যেমন হিসাব বলেছিল, ⌊log₂ 10⁶⌋ + 1 = ২০. Hash table-এ সব ক্ষেত্রে মাত্র ১টি তাক দেখতে হলো।

Hash-এর ১ কেন এত নিখুঁত? এখানে নম্বরগুলো সমান দূরত্বে (৭ করে) বাড়ে, আর table-এর আকার 2²¹; ৭ বিজোড় বলে কোনো দুটি নম্বর একই তাকে পড়েনি — একটিও collision নেই। বাস্তবের এলোমেলো ডেটায় table অর্ধেক ভরা থাকলে linear probing-এ গড়ে দেড়-আড়াইটির মতো তাক দেখতে হয়; সেটিও ১০ লাখের তুলনায় নগণ্য। কিন্তু মনে রাখুন, table বানাতে বাড়তি প্রায় ২০ লাখ ঘরের মেমরি লেগেছে — গতি কিনতে হয়েছে জায়গা দিয়ে। এই সময় বনাম জায়গা-র লেনদেন পুরো কোর্সে বারবার ফিরে আসবে।

মিড-পয়েন্টের সূত্রটি খেয়াল করুন: mid = lo + (hi - lo) / 2, সহজ (lo + hi) / 2 নয়। কারণটি একটি বিখ্যাত বাগের গল্প — ভাবনার প্রশ্ন ৩-এ পড়বেন।

দৌড় প্রতিযোগিতা — নিজে আকার বদলে দেখুন

নিচের ছোট টুলে স্লাইডার টেনে তালিকার আকার ১০ থেকে ১ কোটি পর্যন্ত বদলান, তারপর “নতুন দৌড়” চাপুন। একটি এলোমেলো নম্বর বেছে দুই পদ্ধতিকে একই গতিতে (প্রতি মুহূর্তে সমান সংখ্যক তুলনা) দৌড়াতে দেওয়া হবে। প্রথমবার “Run” চাপলে সবচেয়ে খারাপ ক্ষেত্রটি চলবে — ১০ লাখের শেষ নম্বর।

// Linear vs binary search on the sorted list 0, 1, ..., n-1
const SIZES = [10, 100, 1000, 10000, 100000, 1000000, 10000000];
const $ = (id) => document.getElementById(id);
const fmt = (x) => x.toLocaleString("en-US");
let timer = null;

function binarySteps(n, t) {
  let lo = 0, hi = n - 1, steps = 0;
  while (lo <= hi) {
    const mid = lo + Math.floor((hi - lo) / 2); steps++;
    if (mid === t) break;
    if (mid < t) lo = mid + 1; else hi = mid - 1;
  }
  return steps;
}
function race(n, t) {
  const lin = t + 1, bin = binarySteps(n, t);      // linear checks 0, 1, ..., t
  console.log(`n = ${n}, target = ${t}: linear ${lin}, binary ${bin} comparisons`);
  clearInterval(timer);
  let done = 0;
  const speed = Math.max(1, Math.ceil(lin / 100));   // about 100 frames
  timer = setInterval(() => {
    done = Math.min(lin, done + speed);
    $("lc").textContent = fmt(done);
    $("bc").textContent = Math.min(done, bin);
    $("lb").style.width = (done / n * 100) + "%";
    $("bb").style.width = Math.max(Math.min(done, bin) / n * 100, 0.5) + "%";
    if (done === lin) clearInterval(timer);
  }, 20);
}
$("sz").oninput = () => { $("nv").textContent = fmt(SIZES[$("sz").value]); };
$("go").onclick = () => { const n = SIZES[$("sz").value]; race(n, Math.floor(Math.random() * n)); };
race(1000000, 999999);                               // first run: worst case for linear

প্রথম রানের আউটপুট: n = 1000000, target = 999999: linear 1000000, binary 20 comparisons. সবুজ দাগ (binary) প্রথম মুহূর্তেই ২০-এ থেমে যায়, আর লাল দাগ (linear) প্রায় দুই সেকেন্ড ধরে ১০ লাখ পর্যন্ত গুনতে থাকে। স্লাইডার ১০-এ নামিয়ে দৌড়ালে দুজনের পার্থক্য প্রায় চোখেই পড়ে না — ছোট ইনপুটে সব অ্যালগরিদমই “দ্রুত”। আকার যত বাড়ে, linear-এর কাজ বাড়ে সমান তালে, binary-র বাড়ে প্রতি দশ গুণে মাত্র তিন-চারটি তুলনা। এজন্যই ছোট ইনপুটে পরীক্ষা করে অ্যালগরিদমের গতি বিচার করা যায় না।

৫ · বাস্তব জগতে DSA — আপনি প্রতিদিন যা ব্যবহার করেন

DSA শুধু পরীক্ষা বা কনটেস্টের বিষয় নয়। আপনার ফোনের প্রায় প্রতিটি অ্যাপের ভেতরে এগুলো চুপচাপ কাজ করছে। কোম্পানিগুলোর ভেতরের নির্দিষ্ট প্রযুক্তি সবসময় প্রকাশ্য নয়, তবে এই ধরনের কাজে সাধারণত কোন ধারণাগুলো লাগে, তা জানা আছে:

যেখানে দেখেনসমস্যাটা আসলে কীযে DSA ধারণা কাজে লাগেএই কোর্সে
ম্যাপ অ্যাপে মিরপুর থেকে মতিঝিলের পথরাস্তার মোড়গুলো বিন্দু, রাস্তাগুলো রেখা — সবচেয়ে কম সময়ের পথ খোঁজাgraph, Dijkstra-র মতো shortest path অ্যালগরিদম আর তার দ্রুততর রূপপর্ব ৩৯–৪৪
মোবাইল ব্যাংকিংয়ে (যেমন bKash) সন্দেহজনক লেনদেন ধরাকোটি লেনদেনের মধ্যে অস্বাভাবিক ধরন দ্রুত চেনা — কে কাকে বারবার পাঠাচ্ছেhash table-এ দ্রুত মিল, graph-এ সংযোগ খোঁজা, sliding window-এ সাম্প্রতিক হিসাবপর্ব ১৬, ৩২, ৩৯–৪১
সোশ্যাল মিডিয়ার ফিডহাজারো পোস্ট থেকে আপনার জন্য সেরা কয়েকটি বাছাইheap ও priority queue (সেরা k টি), sorting, graph (বন্ধুত্বের জাল)পর্ব ২৩, ২৫, ৩৯
সার্চ বক্সে লিখতেই পরামর্শ (autocomplete)“dha” লিখলেই “dhaka”, “dhanmondi” দেখানোtrie (prefix-এর tree), sorting আর binary searchপর্ব ১৮, ৩১
ব্যাংক বা বিশ্ববিদ্যালয়ের ডেটাবেসকোটি রেকর্ড থেকে একটি রোল বা অ্যাকাউন্ট তাৎক্ষণিক বের করাB-tree ও B+ tree index, hash indexপর্ব ৩০, ৩২
ZIP ফাইল, PNG ছবিডেটা ছোট করা, কিছু না হারিয়েHuffman coding (DEFLATE পদ্ধতির অংশ), string অ্যালগরিদমপর্ব ৫১, ৫৮

ঢাকার রাস্তার কথা ভাবুন। গুলশান থেকে মোহাম্মদপুর যেতে সম্ভাব্য পথ অসংখ্য। একজন অভিজ্ঞ রিকশাচালক অভিজ্ঞতা থেকে ভালো পথ বেছে নেন। কিন্তু একটি ম্যাপ অ্যাপকে প্রতি সেকেন্ডে লাখো মানুষের জন্য একই কাজ করতে হয় — প্রতিটি মোড়, প্রতিটি রাস্তার তখনকার জ্যাম হিসাবে নিয়ে। সব সম্ভাব্য পথ একটি একটি করে যাচাই করলে উত্তর আসতে আসতে যাত্রাই শেষ হয়ে যেত। Graph অ্যালগরিদম সেই অভিজ্ঞ চালকের বুদ্ধিকে গাণিতিকভাবে নিশ্চিত আর অসম্ভব দ্রুত করে তোলে।

খেয়াল করুন, প্রতিটি উদাহরণে প্রশ্নটা একই রকম: ডেটা অনেক, সময় কম। ১০০টি রেকর্ডে যেকোনো পদ্ধতিই চলে। ১০ কোটি রেকর্ডে শুধু সঠিক data structure আর algorithm-ই চলে। এজন্যই বড় সফটওয়্যার কোম্পানিগুলোর ইন্টারভিউতে DSA-র প্রশ্ন এত বেশি — তাঁরা দেখতে চান, ডেটা বাড়লে আপনার সমাধান টিকবে কি না।

৬ · কীভাবে মাপব — Big-O-র প্রথম ঝলক

“আমার প্রোগ্রাম ০.৫ সেকেন্ডে চলে” — এই কথায় তেমন তথ্য নেই। কোন কম্পিউটারে? কত বড় ইনপুটে? নতুন ল্যাপটপে যা ০.৫ সেকেন্ড, পুরোনোটিতে হয়তো ২ সেকেন্ড। তাই অ্যালগরিদম মাপা হয় অন্যভাবে: ইনপুটের আকার n বাড়লে কাজের পরিমাণ কোন হারে বাড়ে? এই হারকে প্রকাশ করার ভাষার নাম Big-O notation.

  • Time complexity — n-এর তুলনায় ধাপের সংখ্যা কীভাবে বাড়ে। Linear search: O(n), কারণ সবচেয়ে খারাপে প্রতিটি ঘর একবার দেখা হয়। Binary search: O(log n), কারণ প্রতি তুলনায় বাকি অংশ অর্ধেক হয়, আর n কে অর্ধেক করতে করতে ১-এ নামাতে log₂ n বার লাগে।
  • Space complexity — বাড়তি মেমরি কতটা লাগে। Linear ও binary search দুটিই O(1) বাড়তি জায়গা নেয় (শুধু কয়েকটি ভ্যারিয়েবল)। Hash table বানাতে লাগে O(n) বাড়তি জায়গা।
nlog₂ nn log₂ nn²
১০≈ ৩.৩≈ ৩৩১০০
১,০০০≈ ১০≈ ১০,০০০১০ লাখ
১০ লাখ (10⁶)≈ ২০≈ ২ কোটি10¹²
১০০ কোটি (10⁹)≈ ৩০≈ 3 × 10¹⁰10¹⁸

একটি মোটা দাগের আন্দাজ মনে রাখুন: সাধারণ কম্পিউটার সেকেন্ডে মোটামুটি 10⁸ (১০ কোটি) সরল কাজ করতে পারে — এটি নিখুঁত মাপ নয়, শুধু হিসাবের সুবিধার জন্য। সেই হিসাবে n = 10⁶-এ O(n log n) অ্যালগরিদম এক সেকেন্ডের অনেক কম সময় নেয়, কিন্তু O(n²) নেয় প্রায় 10⁴ সেকেন্ড — পৌনে তিন ঘণ্টা! কম্পিউটার দশ গুণ দ্রুত হলেও O(n²) সেখানে টিকবে না; কিন্তু ভালো অ্যালগরিদম পুরোনো কম্পিউটারেও টিকে যায়।

এখানে শুধু ঝলক দেখালাম। Big-O, Ω আর Θ-এর সঠিক সংজ্ঞা, কীভাবে প্রমাণ করতে হয়, আর কোড দেখে complexity বের করার নিয়ম আসবে পর্ব ৪-এ। best, worst আর average case-এর পার্থক্য (যেমন linear search-এ প্রথম ঘরে পাওয়া বনাম না পাওয়া) পর্ব ৫-এ, আর log ও ধারার গণিত পর্ব ৬-এ।

৭ · কোন ভাষায় — C++ আর Python কেন

অ্যালগরিদম কোনো ভাষার সম্পত্তি নয় — binary search C-তে যা, Python-এও তাই। তবু কোড লিখতে একটি ভাষা তো লাগবেই। এই কোর্সে মূল ভাষা দুটি:

C++ — কনটেস্ট আর গতির ভাষা

দ্রুত চলে, মেমরির ওপর নিয়ন্ত্রণ দেয়, আর এর STL (Standard Template Library)-তে vector, map, set, priority_queue, sort, lower_bound — প্রায় সব প্রচলিত data structure আর algorithm তৈরি অবস্থায় আছে। ICPC-সহ প্রায় সব প্রোগ্রামিং কনটেস্টে সবচেয়ে বেশি ব্যবহৃত ভাষা এটি।

আধুনিক C++ (C++20/23), STL

Python — পরিষ্কার চিন্তার ভাষা

কোড প্রায় pseudocode-এর মতো পড়া যায়, তাই অ্যালগরিদমের মূল ভাবনা স্পষ্ট দেখা যায়। ইন্টারভিউতে খুব জনপ্রিয়। তবে বড় লুপে C++-এর চেয়ে অনেক গুণ ধীর হতে পারে — কনটেস্টে সময়সীমা কড়া হলে সমস্যা হয়।

শুধু standard library

প্রতিটি মূল অ্যালগরিদম দুই ভাষাতেই পাবেন, একই ইনপুটে একই আউটপুট দিয়ে — যেমন উপরের ফোন নম্বরের প্রোগ্রাম। যেটিতে স্বচ্ছন্দ, সেটি পড়ুন; অন্যটিতে চোখ বুলালে দুই ভাষার পার্থক্যও শিখে ফেলবেন। C++ নতুন হলে আমাদের C++ প্রোগ্রামিং কোর্স আর বিশেষ করে STL অ্যালগরিদমের পর্বটি কাজে লাগবে; Python-এর জন্য আছে Python প্রোগ্রামিং কোর্স.

Java বা JavaScript-এ লেখেন? আপনিও স্বাগত। এই কোর্সের প্রতিটি ধারণা ভাষা-নিরপেক্ষ; C++-এর vector মানে Java-র ArrayList বা JavaScript-এর array, unordered_map মানে HashMap বা Map. দরকার হলে আমাদের Java কোর্স আর JavaScript কোর্স দেখে নিন। একটি ভালো অনুশীলন: প্রতিটি পর্বের মূল অ্যালগরিদম নিজের ভাষায় আবার লিখুন, আর আউটপুট আমাদের সাথে মিলিয়ে দেখুন।

৮ · রোডম্যাপ — ৯টি মডিউল, ৭২টি পর্ব

পুরো কোর্সটি একটি সিঁড়ির মতো: প্রতিটি মডিউল আগেরটির ওপর দাঁড়িয়ে নতুন কিছু “খুলে” দেয়। “পরে” চেপে মডিউলগুলো একে একে দেখুন:

মডিউল ধরে ধরে
মডিউল
পর্ব
মূল বিষয়
যা খুলে দেয়
  1. ভিত্তি। এখানেই শিখবেন অ্যালগরিদম কী (পর্ব ২), কীভাবে প্রমাণ করবেন সেটি ঠিক (পর্ব ৩), Big-O দিয়ে মাপা (পর্ব ৪), আর recursion ও master theorem. Design & Analysis of Algorithms-এর বিশ্লেষণের অংশ মূলত এই মডিউলে। এটি এড়িয়ে গেলে পরের সব মডিউল আধা-বোঝা থাকবে।
  2. লিনিয়ার স্ট্রাকচার। সারি ধরে সাজানো ডেটা — array থেকে queue. সাথে two pointer, sliding window আর prefix sum — অল্প কৌশলে অনেক সমস্যার O(n²) সমাধানকে O(n) বানানো।
  3. সার্চিং ও সর্টিং। এই পর্বের binary search আসল রূপে আসবে পর্ব ১৮-এ, আর “উত্তরের ওপর binary search” পর্ব ১৯-এ। সব প্রধান sort-এর সাথে প্রমাণ করবেন, তুলনা করে সাজানো কেন n log n-এর চেয়ে দ্রুত হতে পারে না।
  4. ট্রি, হ্যাশ ও DSU. আজকের hash table পূর্ণ রূপে পর্ব ৩২-এ। সাথে নিজে নিজে ভারসাম্য রাখা tree আর ডেটাবেসের B-tree.
  5. রেঞ্জ কোয়েরি। “৩ থেকে ৯০,০০০ নম্বর ঘরের যোগফল কত?” — এমন প্রশ্ন লাখবার, প্রতিবার O(log n)-এ। কনটেস্টে খুব প্রিয় বিষয়।
  6. গ্রাফ। রাস্তা, বন্ধুত্ব, কাজের নির্ভরতা — সবই graph. BFS থেকে network flow পর্যন্ত, প্রমাণসহ।
  7. ডিজাইন কৌশল। নতুন সমস্যা দেখে নিজে অ্যালগরিদম বানানোর চিন্তার পদ্ধতি: divide and conquer, greedy, dynamic programming, backtracking. ইন্টারভিউ আর কনটেস্টের সবচেয়ে বড় অংশ।
  8. উন্নত তত্ত্ব। কোন সমস্যার দ্রুত সমাধান সম্ভবত নেই (NP-hard), আর তখন বাস্তবে কী করা হয়। শেষে একটি ক্যাপস্টোন — একটি কঠিন সমস্যা শুরু থেকে শেষ পর্যন্ত।
  9. পরীক্ষা, ইন্টারভিউ ও কনটেস্ট। হাতে চালিয়ে উত্তর বের করা, MCQ, লিখিত ও ভাইভা প্রশ্ন, কোডিং ইন্টারভিউর প্যাটার্ন আর ICPC-ধাঁচের সমস্যা সেট — বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষা যেটিরই প্রস্তুতি নিন।

লাফিয়ে সরাসরি DP বা গ্রাফে যাওয়ার লোভ হতে পারে। কিন্তু মডিউল ১ বাদ দিলে আপনি অ্যালগরিদম মুখস্থ করবেন, বুঝবেন না — আর মুখস্থ অ্যালগরিদম একটু ভিন্ন প্রশ্নেই অচল। যদি আগে থেকে কিছু জানেন, প্রতিটি পর্বের কুইজ দিয়ে নিজেকে যাচাই করে দ্রুত এগিয়ে যান।

৯ · কীভাবে অনুশীলন করবেন — হাত, মাথা আর online judge

DSA পড়ে শেখার বিষয় নয়, করে শেখার বিষয় — সাঁতারের মতো। বই পড়ে সাঁতার শেখা যায় না। নিচের চারটি অভ্যাস পুরো কোর্সে আপনার সঙ্গী হবে:

  1. হাতে ট্রেস করা — dry run বা ড্রাই রান। খাতা-কলম নিয়ে ছোট একটি ইনপুটে অ্যালগরিদম ধাপে ধাপে চালান — প্রতিটি ভ্যারিয়েবলের মান একটি টেবিলে লিখে। উপরের stepper-গুলো ঠিক এটাই করে; এবার আপনি নিজে করবেন। বিশ্ববিদ্যালয় ও চাকরির লিখিত পরীক্ষায় এই দক্ষতা সরাসরি নম্বর আনে।
  2. Brute force দিয়ে মিলিয়ে দেখা। দ্রুত সমাধান লেখার আগে বা পরে একটি ধীর কিন্তু নিশ্চিতভাবে ঠিক সমাধান লিখুন — যেমন binary search-এর পাশে linear search. ছোট ইনপুটে দুটির উত্তর মিলিয়ে দেখুন। Brute force হলো আপনার “উত্তরপত্র”।
  3. Stress testing. এলোমেলো ছোট ইনপুট বানিয়ে হাজারবার দুটি সমাধান চালানো, যতক্ষণ না কোনো অমিল পাওয়া যায়। অমিল পেলে সেই ছোট ইনপুটটিই আপনার বাগের ঠিকানা। Stress testing কনটেস্ট প্রোগ্রামারদের সবচেয়ে কাজের অস্ত্রগুলোর একটি।
  4. Online judge-এ নিয়মিত সমস্যা। Online judge হলো এমন ওয়েবসাইট, যেখানে সমস্যা পড়ে কোড জমা দিলে সার্ভার গোপন অনেকগুলো ইনপুটে চালিয়ে বলে দেয় — Accepted, Wrong Answer, নাকি Time Limit Exceeded.
প্ল্যাটফর্মযে জন্য পরিচিত
Codeforcesনিয়মিত অনলাইন কনটেস্ট আর রেটিং; বিশাল সমস্যা-ভান্ডার, কঠিনতা অনুযায়ী ছাঁকা যায়
AtCoderজাপানের প্ল্যাটফর্ম; পরিচ্ছন্ন, শিক্ষণীয় সমস্যা, নতুনদের জন্য সহজ স্তরের কনটেস্টও আছে
LeetCodeকোডিং ইন্টারভিউর ধাঁচের সমস্যা, বিষয় ধরে সাজানো
UVa Online Judgeস্পেনের Valladolid বিশ্ববিদ্যালয়ের পুরোনো, বহুল ব্যবহৃত সমস্যা-সংগ্রহ
Tophবাংলাদেশি প্ল্যাটফর্ম; দেশের অনেক বিশ্ববিদ্যালয়ের কনটেস্ট এখানে হয়

বাংলাদেশের কনটেস্ট সংস্কৃতি

Competitive programming-এ বাংলাদেশের একটি সমৃদ্ধ ঐতিহ্য আছে। বহু বছর ধরে ঢাকায় আন্তর্জাতিক কলেজিয়েট প্রোগ্রামিং কনটেস্টের এশীয় আঞ্চলিক পর্ব — ICPC Asia Dhaka Regional — অনুষ্ঠিত হয়, যেখানে দেশ-বিদেশের বিশ্ববিদ্যালয়ের দল অংশ নেয়, আর বাংলাদেশের দলগুলো নিয়মিত ICPC World Finals-এ খেলেছে। দেশের ভেতরে আছে জাতীয় কলেজিয়েট প্রোগ্রামিং কনটেস্ট (NCPC) আর বিভিন্ন বিশ্ববিদ্যালয়ের আয়োজনে অসংখ্য IUPC (Inter University Programming Contest). স্কুল-কলেজের শিক্ষার্থীদের জন্য আছে জাতীয় হাই স্কুল প্রোগ্রামিং কনটেস্ট আর ইনফরমেটিক্স অলিম্পিয়াড। এসব কনটেস্টের সমস্যার হৃদয়ই হলো DSA.

আরও গভীরে — binary search কি সাজানো তালিকায় খোঁজার সেরা উপায়? শুধু “তুলনা” করে খোঁজে এমন যেকোনো অ্যালগরিদমকে একটি decision tree হিসেবে আঁকা যায়, যার প্রতিটি পাতায় একটি সম্ভাব্য উত্তর। n টি ঘর আর “নেই” মিলিয়ে অন্তত n + 1টি পাতা লাগে, আর উচ্চতা h হলে পাতা সর্বোচ্চ 2ʰ; তাই সবচেয়ে খারাপে অন্তত ⌈log₂(n + 1)⌉টি তুলনা লাগবেই — binary search ঠিক এই সীমায় পৌঁছায়, তাই সে সর্বোত্তম। Hash table এই সীমা ভাঙে, কারণ সে শুধু তুলনা করে না — নাম থেকে ঠিকানা হিসাব করে। একই ধরনের যুক্তিতে পর্ব ২৪-এ প্রমাণ করবেন, তুলনা-ভিত্তিক sort-এ Ω(n log n) লাগে।

পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা কোডিং ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন আসতে পারে:

  • Data structure আর algorithm-এর পার্থক্য কী? প্রথমটি ডেটা সাজানোর ও তার ওপর কাজের নিয়ম, দ্বিতীয়টি সমস্যা সমাধানের সীমিত, সুনির্দিষ্ট ধাপের ক্রম।
  • একটি অ্যালগরিদমের বৈশিষ্ট্যগুলো কী কী? Knuth-এর তালিকা অনুযায়ী পাঁচটি: input, output, definiteness (প্রতিটি ধাপ স্পষ্ট), finiteness (সীমিত ধাপে শেষ) আর effectiveness (প্রতিটি ধাপ সত্যিই করা যায়)।
  • Linear আর non-linear data structure-এর উদাহরণ দিন। Linear: array, linked list, stack, queue; non-linear: tree, graph.
  • Linear ও binary search-এর worst-case complexity? O(n) আর O(log n); binary search-এর শর্ত — তালিকা সাজানো।
  • Hash table-এ খোঁজা গড়ে O(1), তবু কখন BST বা সাজানো array ভালো? যখন ক্রম-নির্ভর প্রশ্ন লাগে (পরের বড় মান, রেঞ্জ) বা worst-case-এর নিশ্চয়তা দরকার।

১০ · AI-এর যুগে DSA — সহকারী আছে, বিচারক আপনি

আজ যেকোনো AI-কে বললেই binary search বা Dijkstra-র কোড লিখে দেয়। তাহলে DSA শেখার দরকার কী? উত্তর: AI খুব দ্রুত লিখতে পারে, কিন্তু সে যা লেখে তা ঠিক কি না, যথেষ্ট দ্রুত কি না, আর আপনার সমস্যার জন্যই উপযুক্ত কি না — এই বিচার করতে হয় আপনাকে। DSA না জানলে আপনি সেই বিচার করতে পারবেন না; তখন AI-এর ভুলও আপনার ভুল হয়ে যাবে।

DSA-তে AI সাধারণত যেসব ভুল করে

AI-এর ভুলউদাহরণকীভাবে ধরবেন
ভুল complexity দাবিলুপের ভেতরে list.index() বা erase রেখে বলে “O(n)” — আসলে O(n²)প্রতিটি লাইন n-এর সাথে কতবার চলে, নিজে গুনুন
Off-by-one ভুলlo < hi আর lo <= hi গুলিয়ে ফেলা; শেষ ঘর বা এক-ঘরের তালিকায় ভুলn = 0, 1, 2-এর তালিকায় হাতে ট্রেস
প্রমাণহীন greedy“প্রতিবার সবচেয়ে বড়টা নাও” — শুনতে যৌক্তিক, কিন্তু অনেক ইনপুটে ভুলছোট ইনপুটে brute force-এর সাথে stress test
Overflow আর edge case উপেক্ষা(lo + hi) / 2 বড় index-এ overflow; খালি তালিকা ভুলে যাওয়াসবচেয়ে বড় আর সবচেয়ে ছোট সীমার ইনপুট দিয়ে চালানো

AI-কে সমাধান লিখিয়ে নেওয়ার চেয়ে শিক্ষক হিসেবে ব্যবহার করুন — আগে নিজে ভাবুন, তারপর আপনার চিন্তা যাচাই করান। নিচের কাঠামোয় প্রশ্ন করুন:

আমি DSA শিখছি। সমস্যা: একটি সাজানো array-তে একটি সংখ্যা প্রথম কোন index-এ আছে, বের করতে হবে (না থাকলে -1). আমার চিন্তা: binary search চালাব, কিন্তু মিলে গেলেও থামব না — উত্তর মনে রেখে বাঁ দিকে খুঁজতে থাকব। আমার হিসাবে time complexity O(log n). ১. আমার পদ্ধতি কি ঠিক? পুরো কোড দিও না, শুধু কোথায় ভুল হতে পারে বলো। ২. কোন কোন edge case-এ আমার কোড পরীক্ষা করা উচিত, তালিকা দাও। ৩. আমার complexity-র যুক্তি ঠিক কি না, এক লাইনে কারণসহ বলো। ৪. ছোট এলোমেলো ইনপুটে brute force-এর সাথে মিলিয়ে দেখার জন্য একটি stress test-এর কাঠামো বুঝিয়ে দাও।

AI-এর দেওয়া অ্যালগরিদম হাতে পেলে — যাচাইয়ের চেকলিস্ট

কনটেস্ট চলাকালীন AI ব্যবহারের নিয়ম আলাদা আলাদা — অনেক কনটেস্টে এটি সম্পূর্ণ নিষিদ্ধ। কোনো কনটেস্টে অংশ নেওয়ার আগে তার নিয়ম পড়ে নিন। অনুশীলনে AI-কে শিক্ষক বানান, প্রতিযোগিতায় নিজের মাথাই ভরসা।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১Wirth বললেন “Algorithms + Data Structures = Programs” — কিন্তু অভিজ্ঞ প্রোগ্রামাররা কেন বলেন, data structure ঠিক হলে algorithm প্রায় নিজে থেকেই চলে আসে?

Niklaus Wirth ১৯৭৬ সালের বইয়ে সমীকরণটি দেন, কিন্তু পরের প্রজন্মের অনেক প্রকৌশলী এর একটি দিকে বাড়তি জোর দিয়েছেন — ডেটার দিকে। ১৯৮৯ সালে Rob Pike (পরে Go ভাষার সহ-স্রষ্টা) তাঁর “Notes on Programming in C”-তে লেখেন: ডেটাই প্রধান; সঠিক data structure বেছে নিলে অ্যালগরিদম প্রায় স্বতঃস্পষ্ট হয়ে যায়। আর Linux-এর স্রষ্টা Linus Torvalds একবার মেইলিং লিস্টে লিখেছিলেন, খারাপ প্রোগ্রামাররা কোড নিয়ে চিন্তা করেন, ভালো প্রোগ্রামাররা চিন্তা করেন data structure আর তাদের সম্পর্ক নিয়ে।

কেন এমন হয়? এই পর্বের উদাহরণটিই দেখুন। নম্বরগুলো এলোমেলো array-তে থাকলে খোঁজার একমাত্র উপায় linear search — অন্য কোনো বুদ্ধি খাটানোর সুযোগই নেই। সাজানো array হলে binary search “স্বাভাবিকভাবে” মাথায় আসে। Hash table হলে খোঁজা মানে শুধু ঠিকানা হিসাব। অর্থাৎ structure ঠিক করে দেয় কোন কাজ সস্তা আর কোনটা দামি; algorithm শুধু সেই সস্তা কাজগুলো জুড়ে দেয়।

বড় সফটওয়্যারেও একই সত্য। ডেটাবেসের নকশায় কোন কলামে index থাকবে, ক্যাশে কী রাখা হবে, গ্রাফ adjacency list-এ রাখা হবে না matrix-এ — এই সিদ্ধান্তগুলো পরে বদলানো কঠিন ও ব্যয়বহুল, আর এগুলোই পুরো সিস্টেমের গতি ঠিক করে দেয়। একটি ধীর ফাংশন পরে আবার লেখা যায়; ভুল ডেটা-কাঠামোর ওপর দাঁড়ানো পুরো সিস্টেম বদলানো অনেক কঠিন।

তবে উল্টো দিকটাও সত্য: একই structure-এর ওপর ভালো আর খারাপ algorithm দুটোই চলে। সাজানো তালিকাতেও linear search চালানো যায়। তাই Wirth-এর সমীকরণে যোগ চিহ্নটিই আসল — দুটি একসাথে নকশা করতে হয়।

মূল উপলব্ধি: নতুন সমস্যায় প্রথমে জিজ্ঞেস করুন, “কোন কাজগুলো বারবার করতে হবে, আর কোন structure সেগুলোকে সস্তা করে?” — algorithm তার পরের প্রশ্ন।

প্র ০২কম্পিউটার প্রতি বছর দ্রুত হচ্ছে — তাহলে ভালো অ্যালগরিদম নিয়ে এত মাথাব্যথা কেন? দ্রুত হার্ডওয়্যার কিনলেই তো হয়!

কয়েক দশক ধরে চিপে ট্রানজিস্টরের সংখ্যা মোটামুটি প্রতি দুই বছরে দ্বিগুণ হয়েছে — Intel-এর সহ-প্রতিষ্ঠাতা Gordon Moore-এর ১৯৬৫ সালের পর্যবেক্ষণ থেকে একে “Moore’s law” বলা হয়। তবু অ্যালগরিদমের গুরুত্ব কমেনি, বরং বেড়েছে। কারণটি একটি সরল হিসাবে লুকিয়ে আছে।

ধরুন আপনার O(n²) অ্যালগরিদম এক সেকেন্ডে n = ১০,০০০ পর্যন্ত সামলাতে পারে। এবার কম্পিউটার ১০০ গুণ দ্রুত হলো। নতুন কম্পিউটারে এক সেকেন্ডে কত বড় n? n² কে ১০০ গুণ করতে n বাড়ে মাত্র ১০ গুণ — ১ লাখ পর্যন্ত। অথচ একটি O(n log n) অ্যালগরিদম পুরোনো কম্পিউটারেই এক সেকেন্ডে কয়েক লাখ থেকে কোটির ঘরের n সামলায়। অর্থাৎ ভালো অ্যালগরিদম যে লাফ দেয়, শত গুণ দামি হার্ডওয়্যারও তা দিতে পারে না। আর O(2ⁿ)-এর ক্ষেত্রে ১০০ গুণ দ্রুত কম্পিউটার n বাড়ায় মাত্র ৬-৭ — প্রায় কিছুই না।

তার ওপর ডেটা হার্ডওয়্যারের চেয়েও দ্রুত বাড়ছে। মোবাইল ব্যাংকিং, অনলাইন কেনাকাটা, সোশ্যাল মিডিয়া — প্রতিদিন কোটি কোটি নতুন রেকর্ড। আর Moore’s law-এর গতিও গত দশকে ধীর হয়ে এসেছে; একক প্রসেসরের গতি আগের মতো লাফিয়ে বাড়ছে না। খরচের দিকটাও আছে: ক্লাউডে প্রতিটি সার্ভার-ঘণ্টার দাম দিতে হয়, আর ফোনের ব্যাটারি সীমিত। একটি অ্যালগরিদম দশ গুণ কম কাজ করলে বিদ্যুৎ বিল, সার্ভারের সংখ্যা আর ব্যাটারির খরচও কমে।

ইতিহাসেও এর উদাহরণ আছে: বহু বৈজ্ঞানিক গণনায় গত কয়েক দশকে অ্যালগরিদমের উন্নতি থেকে যে গতি এসেছে, তা অনেক ক্ষেত্রে হার্ডওয়্যারের উন্নতির সমান বা বেশি — যেমন ১৯৬৫ সালে Cooley ও Tukey-র প্রকাশিত FFT, যা একটি O(n²) গণনাকে O(n log n)-এ নামিয়ে সংকেত-প্রক্রিয়াকরণের পুরো জগৎ বদলে দেয়।

মূল উপলব্ধি: হার্ডওয়্যার ধ্রুবক গুণে সাহায্য করে; অ্যালগরিদম বদলায় বৃদ্ধির হার। বড় n-এ হার সবসময় জেতে।

প্র ০৩Binary search তো দশ লাইনের কোড — তবু কেন বলা হয় পেশাদার প্রোগ্রামাররাও এটি প্রায়ই ভুল লেখেন?

Binary search-এর ধারণা পুরোনো — কম্পিউটার বিজ্ঞানে এর প্রথম উল্লেখ পাওয়া যায় ১৯৪৬ সালে John Mauchly-র বক্তৃতায়। কিন্তু Donald Knuth তাঁর The Art of Computer Programming-এ লক্ষ করেন, সব n-এর জন্য সঠিকভাবে কাজ করে এমন সংস্করণ প্রকাশ হতে আরও প্রায় দেড় দশক লেগেছিল — সাধারণত এর কৃতিত্ব দেওয়া হয় D. H. Lehmer-এর ১৯৬০ সালের সংস্করণকে। Jon Bentley তাঁর Programming Pearls বইয়ে লেখেন, পেশাদার প্রোগ্রামারদের কোর্সে তিনি binary search লিখতে দিয়ে দেখেছেন — বেশিরভাগের কোডেই কোনো না কোনো বাগ ছিল।

ভুলগুলো কোথায় হয়? প্রায় সবসময় সীমানায়: lo < hi না lo <= hi; hi = mid না hi = mid - 1; খালি তালিকা বা এক-ঘরের তালিকা; কখনো লুপ আর থামেই না, কারণ lo বা hi একবারও বদলায় না।

সবচেয়ে বিখ্যাত গল্পটি ২০০৬ সালের। Google-এর প্রকৌশলী Joshua Bloch একটি ব্লগ লেখায় জানান, Java-র standard library-র Arrays.binarySearch-এ প্রায় নয় বছর ধরে একটি বাগ ছিল — এমনকি Bentley-র বইয়ের সংস্করণেও একই সমস্যা। লাইনটি ছিল mid = (low + high) / 2. তালিকায় একশো কোটির বেশি উপাদান থাকলে low + high ৩২-বিট int-এর সর্বোচ্চ মান (প্রায় ২১৫ কোটি) ছাড়িয়ে ঋণাত্মক হয়ে যায় — overflow. তখন ছোট তালিকা নিয়ে কেউ এটি টের পায়নি; বড় ডেটার যুগে এসে ধরা পড়ল। সমাধান: mid = low + (high - low) / 2 — এই পর্বের কোডে যেটি ব্যবহার করেছি।

শিক্ষাটি গভীর: সহজ দেখতে অ্যালগরিদমেও শুদ্ধতা প্রমাণ করতে হয়। পর্ব ৩-এ শিখবেন loop invariant — যেমন “উত্তর থাকলে সবসময় [lo, hi]-এর ভেতরেই আছে” — যা প্রতিটি সীমানা-সিদ্ধান্তকে যুক্তি দিয়ে বেঁধে দেয়।

মূল উপলব্ধি: “কোড চলছে” আর “কোড ঠিক” এক কথা নয়। Invariant দিয়ে ভাবুন, সীমানার ইনপুটে পরীক্ষা করুন, আর বড় ডেটার কথা মাথায় রাখুন।

প্র ০৪Hash table-এ খোঁজা গড়ে O(1) হলে tree বা সাজানো array-র দরকার কী? Hash কি কখনো বিপদও ডেকে আনতে পারে?

Hash table-এর ধারণা অনেক পুরোনো — ১৯৫৩ সালে IBM-এর Hans Peter Luhn একটি অভ্যন্তরীণ নোটে তাক-ভিত্তিক hashing-এর প্রস্তাব দেন বলে জানা যায়। আজ প্রায় প্রতিটি ভাষায় এটি তৈরি অবস্থায় আছে: Python-এর dict, C++-এর unordered_map, Java-র HashMap. তবু hash সব প্রশ্নের উত্তর নয়, তিনটি কারণে।

১. ক্রম হারিয়ে যায়। Hash table জানে না কোন নাম কার পরে। “৫০০ থেকে ৮০০ টাকার সব লেনদেন”, “সবচেয়ে ছোট রোল”, “এই নামের পরের নাম” — এসব প্রশ্নে hash অসহায়। সাজানো array বা balanced BST (C++-এর map/set) এগুলো O(log n)-এ পারে। এজন্যই ডেটাবেসের index সাধারণত B+ tree, যা রেঞ্জের প্রশ্নও দ্রুত সামলায়।

২. গড় আর নিশ্চয়তা আলাদা। O(1) হলো গড়। দুর্ভাগ্যক্রমে বা খারাপ hash function-এ সব key একই তাকে পড়লে খোঁজা O(n) — এই পর্বের “সবার নাম M দিয়ে শুরু” উদাহরণের মতো। Balanced tree-তে সবচেয়ে খারাপেও O(log n)-এর নিশ্চয়তা থাকে।

৩. ইচ্ছাকৃত আক্রমণ। ২০১১ সালের শেষে নিরাপত্তা গবেষকেরা দেখান, অনেক জনপ্রিয় ওয়েব-প্ল্যাটফর্মে hash function অনুমানযোগ্য হওয়ায় আক্রমণকারী এমন হাজারো key পাঠাতে পারে যেগুলো সব একই তাকে পড়ে। তখন সার্ভার প্রতিটি অনুরোধে O(n²) কাজ করতে গিয়ে কার্যত অচল হয়ে যায় — একে বলে hash flooding. এর জবাবে Python ৩.৩ থেকে string-এর hash প্রতিবার প্রোগ্রাম চালু হওয়ার সময় এলোমেলো বীজ দিয়ে হিসাব করে, যাতে বাইরের কেউ আগে থেকে collision বানাতে না পারে। কনটেস্টেও একই কৌশল দেখা যায়: কেউ কেউ ইচ্ছা করে এমন টেস্ট বানান যাতে unordered_map ধীর হয়ে যায়।

মূল উপলব্ধি: গড় গতি, সবচেয়ে খারাপের নিশ্চয়তা, ক্রম-নির্ভর প্রশ্ন আর নিরাপত্তা — চারটিই বিবেচনা করে structure বাছতে হয়। “সবসময় hash” যেমন ভুল, “কখনো hash নয়”-ও তেমনি ভুল।

প্র ০৫কনটেস্ট প্রোগ্রামিং কি “আসল” সফটওয়্যার তৈরির থেকে আলাদা কিছু? এতে সময় দেওয়ার মূল্য কোথায়?

ICPC-র শুরু ১৯৭০ সালে যুক্তরাষ্ট্রের Texas A&M বিশ্ববিদ্যালয়ে একটি প্রতিযোগিতা হিসেবে; ১৯৭৭ সাল থেকে এর World Finals হয়ে আসছে, আর আজ এটি বিশ্বের সবচেয়ে বড় বিশ্ববিদ্যালয়-পর্যায়ের প্রোগ্রামিং প্রতিযোগিতাগুলোর একটি। তিনজনের একটি দল, একটি কম্পিউটার, কয়েক ঘণ্টা আর এক ডজনের মতো সমস্যা — এই চাপের মধ্যে দলগত সমাধান।

পার্থক্য সত্যিই আছে। কনটেস্টের কোড সাধারণত একবার লেখা হয়, কেউ পরে পড়ে না; ভ্যারিয়েবলের নাম এক অক্ষরের, কোনো ডকুমেন্টেশন নেই, টেস্ট লেখার সময় নেই। আসল সফটওয়্যারে উল্টো — কোড বছরের পর বছর বহু মানুষ পড়েন ও বদলান, তাই পাঠযোগ্যতা, টেস্ট, নিরাপত্তা আর দলগত কাজই বেশি গুরুত্বপূর্ণ। আর বাস্তবের বেশিরভাগ কাজে segment tree লেখার দরকার পড়ে না।

তবু মূল্য বিরাট। কনটেস্ট আপনাকে শেখায় সমস্যা থেকে মডেল বের করা (“এটি আসলে একটি shortest path”), constraint দেখে complexity আন্দাজ করা (“n = 2 × 10⁵, তাই O(n²) চলবে না”), সীমানার ক্ষেত্র ভাবা আর চাপের মধ্যে ভুল খুঁজে বের করা। এই দক্ষতাগুলোই কোডিং ইন্টারভিউতে সরাসরি যাচাই করা হয়, আর বাস্তবের পারফরম্যান্স-সমস্যাতেও কাজে লাগে।

বাংলাদেশে এই সংস্কৃতি দীর্ঘদিনের। ঢাকায় ICPC-র আঞ্চলিক পর্ব, জাতীয় পর্যায়ের NCPC আর অসংখ্য IUPC থেকে বহু প্রোগ্রামার দেশ-বিদেশের বড় প্রযুক্তি প্রতিষ্ঠানে কাজ করছেন, কেউ কেউ নিজেরাই প্রতিষ্ঠান গড়েছেন; Toph-এর মতো দেশীয় প্ল্যাটফর্মও এই সংস্কৃতি থেকেই এসেছে।

মূল উপলব্ধি: কনটেস্ট হলো DSA-র জিমনেশিয়াম — পেশি বানায়, কিন্তু খেলার মাঠ নয়। দুটোই করুন: কনটেস্টে গতি ও চিন্তা, প্রজেক্টে পরিচ্ছন্ন ও টেকসই কোড।

অনুশীলন

খাতা-কলম হাতে নিন। আগে নিজে করুন, তারপর উত্তর মেলান।

  1. হাতে ট্রেস করুন: সাজানো array [3, 8, 15, 21, 27, 34, 42, 56, 63, 71] (index 0 থেকে 9)-এ binary search চালিয়ে (ক) 56 আর (খ) 10 খুঁজুন। প্রতিটি ধাপে lo, hi, mid আর a[mid] একটি টেবিলে লিখুন; mid = lo + (hi - lo) / 2 (পূর্ণসংখ্যার ভাগ)।

    (ক) 56: lo = 0, hi = 9 → mid = 4, a[4] = 27, 27 ছোট, তাই lo = 5. তারপর mid = 5 + (9 − 5) / 2 = 7, a[7] = 56 — পাওয়া গেল, index 7, মোট ২টি তুলনা।

    (খ) 10: mid = 4, a[4] = 27 বড়, hi = 3. mid = 0 + (3 − 0) / 2 = 1, a[1] = 8 ছোট, lo = 2. mid = 2 + (3 − 2) / 2 = 2, a[2] = 15 বড়, hi = 1. এখন lo = 2 > hi = 1, লুপ শেষ — নেই, ৩টি তুলনা। খেয়াল করুন, শেষে lo = 2 ঠিক সেই জায়গা, যেখানে 10 বসালে তালিকা সাজানো থাকত — এই তথ্যটি পর্ব ১৮-১৯-এ খুব কাজে লাগবে।

  2. হিসাব করে বলুন: একটি তালিকায় ১০ লাখ নম্বর, আর একই তালিকায় ১ লাখ বার খুঁজতে হবে। কম্পিউটার সেকেন্ডে মোটামুটি 10⁸ সরল কাজ করে ধরে নিন। (ক) প্রতিবার linear search করলে সবচেয়ে খারাপে মোট কত সময়? (খ) একবার সাজিয়ে (প্রায় n log₂ n ধাপ) তারপর প্রতিবার binary search করলে?

    (ক) 10⁵ × 10⁶ = 10¹¹ তুলনা → 10¹¹ / 10⁸ = ১,০০০ সেকেন্ড, প্রায় ১৭ মিনিট।

    (খ) সাজাতে প্রায় 10⁶ × 20 = 2 × 10⁷ ধাপ, তারপর 10⁵ × 20 = 2 × 10⁶ তুলনা। মোট প্রায় 2.2 × 10⁷ → ০.২ সেকেন্ডের মতো। একবার সাজানোর খরচ বহুবার খোঁজায় উসুল হয়ে যায় — প্রায় পাঁচ হাজার গুণ দ্রুত। কিন্তু খুঁজতে হতো মাত্র একবার? তখন সাজানোর 2 × 10⁷ ধাপের চেয়ে একটি linear search-এর 10⁶ ধাপই সস্তা।

  3. কোড লিখুন: একটি ফাংশন লিখুন count_le(a, x), যা সাজানো array a-তে x-এর সমান বা ছোট কয়টি সংখ্যা আছে, তা O(log n)-এ ফেরত দেয়। তারপর একটি brute force (পুরো array গুনে দেখা) লিখে ছোট এলোমেলো ইনপুটে দুটির উত্তর মিলিয়ে দেখুন।

    মূল চিন্তা: “a[mid] ≤ x” সত্য হলে উত্তর অন্তত mid + 1, তাই ডানে যান; নইলে বাঁয়ে। লুপ শেষে lo-ই উত্তর।

    int count_le(const std::vector<int>& a, int x) {
        int lo = 0, hi = (int)a.size() - 1;
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            if (a[mid] <= x) lo = mid + 1;   // a[0..mid] all <= x
            else hi = mid - 1;
        }
        return lo;                           // first index with a[i] > x
    }
    // brute force: count every element <= x, then compare on random arrays
    // (STL shortcut: std::upper_bound(a.begin(), a.end(), x) - a.begin())

    Python-এ একই কাজ bisect.bisect_right(a, x). Stress test: ১,০০০ বার ১-১০ আকারের এলোমেলো সাজানো array আর এলোমেলো x নিয়ে দুটি ফাংশনের উত্তর মেলান; খালি array (উত্তর 0) আর সব উপাদান x-এর চেয়ে ছোট (উত্তর n) — এই দুটি ক্ষেত্র আলাদা করে অবশ্যই দেখুন।

  4. সঠিক structure বাছুন: প্রতিটি কাজের জন্য কোন data structure মানানসই, আর কেন? (ক) ট্রেনের টিকিট কাউন্টারের লাইন — যে আগে এসেছে সে আগে টিকিট পাবে। (খ) লেখার অ্যাপের “Undo” বোতাম। (গ) রোল নম্বর দিলে তাৎক্ষণিক নাম দেখানো। (ঘ) লাইভ খেলায় সবসময় সর্বোচ্চ ১০ জনের স্কোর দেখানো। (ঙ) অভিধান অ্যাপে “com” লিখলেই ওই অক্ষরে শুরু হওয়া সব শব্দ।
    • (ক) Queue — আগে এলে আগে বের হয় (পর্ব ১৪)।
    • (খ) Stack — শেষ কাজটি আগে বাতিল হয় (পর্ব ১৩)।
    • (গ) Hash table — key থেকে সরাসরি মান, গড়ে O(1) (পর্ব ৩২)।
    • (ঘ) Heap / priority queue — সর্বোচ্চ মান দ্রুত পাওয়া ও নতুন স্কোর O(log n)-এ যোগ (পর্ব ২৩)।
    • (ঙ) Trie, অথবা সাজানো তালিকায় binary search দিয়ে prefix-এর শুরুর জায়গা বের করা (পর্ব ৩১, ১৮)।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি নতুন অ্যালগরিদম-সমস্যা সমাধানের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. সমস্যা আর ইনপুটের সীমা (constraint) মন দিয়ে পড়া
  2. একটি সরল brute force সমাধান ভাবা
  3. সীমা অনুযায়ী দ্রুততর algorithm ও data structure বাছা
  4. ছোট উদাহরণে খাতায় হাতে ট্রেস করা
  5. কোড লিখে brute force-এর সাথে stress test করা
  6. Online judge-এ জমা দেওয়া

১০ লাখ সাজানো নম্বরে binary search চালালে সবচেয়ে খারাপে মোটামুটি কয়টি তুলনা লাগে?

  • প্রায় ১০ লাখ
  • প্রায় ২০
  • প্রায় ৫ লাখ
  • প্রায় ১,০০০

নিচের কোনটি data structure নয়, বরং একটি algorithm?

  • Stack
  • Queue
  • Binary search
  • Hash table

Binary search সঠিকভাবে কাজ করার জন্য কোন শর্তটি অবশ্যই লাগে?

  • তালিকাটি সাজানো থাকতে হবে
  • উপাদানের সংখ্যা 2-এর ঘাত হতে হবে
  • সব উপাদান আলাদা হতে হবে
  • আগে একটি hash table বানাতে হবে

Hash table-এ একটি key খোঁজার সময় সম্পর্কে কোন কথাটি সঠিক?

  • সবসময়, সব ইনপুটে নিশ্চিতভাবে O(1)
  • সবসময় O(log n), কারণ এটি ভেতরে binary search করে
  • গড়ে O(n), কারণ সব key দেখতে হয়
  • গড়ে O(1), কিন্তু সবচেয়ে খারাপে O(n)

একটি সমস্যায় n সর্বোচ্চ 2 × 105, সময়সীমা ১ সেকেন্ড। কম্পিউটার সেকেন্ডে মোটামুটি 108 সরল কাজ করে ধরলে কোন complexity-র সমাধান নিরাপদ?

  • O(n²)
  • O(n³)
  • O(n log n)
  • O(2n)

Niklaus Wirth-এর ১৯৭৬ সালের বিখ্যাত বইয়ের শিরোনামে কোন সমীকরণটি আছে?

  • Hardware + Software = Computer
  • Algorithms + Data Structures = Programs
  • Code + Tests = Software
  • Time + Space = Complexity

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Data structure = ডেটা সাজানোর নিয়ম; algorithm = সমস্যা সমাধানের সুনির্দিষ্ট ধাপ। দুটি মিলেই প্রোগ্রাম।
  2. ১০ লাখে একটি নম্বর খুঁজতে: linear search সবচেয়ে খারাপে ১০ লাখ, binary search ২০, hash table গড়ে ১-২টি তুলনা।
  3. প্রতিটি দ্রুততার একটি দাম আছে — সাজানোর খরচ, বাড়তি মেমরি, ক্রম হারানো। কাজ অনুযায়ী structure বাছুন।
  4. অ্যালগরিদম মাপা হয় বৃদ্ধির হার দিয়ে (Big-O), সেকেন্ড দিয়ে নয়; বড় n-এ হারই জেতে।
  5. শেখার পথ: হাতে ট্রেস → brute force → stress test → online judge; AI-এর কোডও একইভাবে যাচাই করুন।

পরের পর্বে: “অ্যালগরিদম” শব্দটি আজ বহুবার বললাম — কিন্তু ঠিক কোন জিনিসকে অ্যালগরিদম বলা যায়? পরের পর্বে দেখবেন অ্যালগরিদমের বৈশিষ্ট্য, ভাষা-নিরপেক্ষভাবে লেখার pseudocode, আর ধাপ গোনার জন্য কম্পিউটারের সরল মডেল — RAM model.

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান