Linear & Binary Search
লিনিয়ার ও বাইনারি সার্চ
1. The Search Problem
Searching is the most common operation in computing. Given a collection of items and a target, we want to know: does the target exist, and if so, where? From looking up a phone number in your contacts to finding a row in a database, every system relies on search.
Binary search-এর মাত্র ৫ লাইন কোড — কিন্তু off-by-one bug এড়াতে নিয়মটা শিখলে সারা জীবন কাজে লাগে।
2. Linear Search — The Honest Approach
Linear search examines every element from index 0 to n-1 until it finds the target or exhausts the array. It works on any collection — sorted or unsorted, array or linked list. Time complexity is O(n).
#include <bits/stdc++.h>
using namespace std;
int linearSearch(const vector<int>& a, int target) {
for (int i = 0; i < (int)a.size(); i++) {
if (a[i] == target) return i;
}
return -1;
}
int main() {
vector<int> a = {7, 3, 9, 1, 42, 5};
cout << "index of 42 = " << linearSearch(a, 42) << "\n";
cout << "index of 99 = " << linearSearch(a, 99) << "\n";
return 0;
}
3. Binary Search — Halving the World
If the array is sorted, we can do dramatically better. Compare the target to the middle element: if equal — done; if smaller — search the left half; if larger — search the right half. Each step throws away half the search space, giving O(log n) time.
#include <bits/stdc++.h>
using namespace std;
// Iterative binary search — returns index or -1.
int binarySearch(const vector<int>& a, int target) {
int lo = 0, hi = (int)a.size() - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2; // avoid overflow
if (a[mid] == target) return mid;
if (a[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;
}
int main() {
vector<int> a = {2,5,8,12,16,23,38,42,56,63,71,77,82,85,90,99};
cout << "index of 63 = " << binarySearch(a, 63) << "\n";
cout << "index of 50 = " << binarySearch(a, 50) << "\n";
return 0;
}
mid = lo + (hi - lo) / 2 instead of (lo+hi)/2 — for very large lo + hi the second form overflows.
খুব বড়
lo + hi-এর জন্য (lo+hi)/2 overflow করতে পারে। তাই lo + (hi-lo)/2 লেখাই নিরাপদ।
4. Lower Bound & Upper Bound
lower_bound(x) returns the first index i such that a[i] >= x.
upper_bound(x) returns the first index i such that a[i] > x.
These two functions are the Swiss army knife of competitive programming — count occurrences,
find insertion point, range queries, all reduce to them.
lower_bound(x) এমন প্রথম index i ফেরত দেয় যেখানে a[i] >= x।
upper_bound(x) দেয় প্রথম index যেখানে a[i] > x। এই দুটি ফাংশন competitive programming-এ
অসংখ্য সমস্যা সমাধান করে — count, insertion point, range query সব।
#include <bits/stdc++.h>
using namespace std;
// First i with a[i] >= x
int lowerBound(const vector<int>& a, int x) {
int lo = 0, hi = (int)a.size(); // note: hi = n, not n-1
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] < x) lo = mid + 1;
else hi = mid;
}
return lo;
}
// First i with a[i] > x
int upperBound(const vector<int>& a, int x) {
int lo = 0, hi = (int)a.size();
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] <= x) lo = mid + 1;
else hi = mid;
}
return lo;
}
int main() {
vector<int> a = {1,2,2,2,5,7,9};
cout << "lower_bound(2) = " << lowerBound(a, 2) << "\n";
cout << "upper_bound(2) = " << upperBound(a, 2) << "\n";
cout << "count of 2 = " << (upperBound(a,2) - lowerBound(a,2)) << "\n";
return 0;
}
| Function | Returns | বাংলায় |
|---|---|---|
lower_bound(x) | first i with a[i] ≥ x | প্রথম index যেখানে value x বা তার বেশি |
upper_bound(x) | first i with a[i] > x | প্রথম index যেখানে value x-এর চেয়ে বড় |
upper_bound − lower_bound | count of x | x সংখ্যা কতবার আছে |
lower_bound − 1 | largest index with value < x | x-এর চেয়ে ছোট সর্বশেষ index |
5. Binary Search on the Answer
The most powerful generalisation: if a problem has a monotone predicate
P(x) (false for small x, true for large x — or vice versa),
we can binary-search for the smallest x where P holds. The classic example: Allocate Books.
Problem. Given n books with page counts and k students, allocate
consecutive books to each student so the maximum pages assigned to any student is minimised.
n-টি বইয়ের page সংখ্যা এবং k জন ছাত্র দেওয়া আছে। প্রতি ছাত্রকে ধারাবাহিক বই বরাদ্দ করতে হবে
এমনভাবে যাতে কোনো একজন ছাত্রের সর্বাধিক page-সংখ্যা সর্বনিম্ন হয়। এটিই Bangladesh-এর textbook board-এ বই বিতরণের
মতো একটি সমস্যা।
M is feasible for k students, then any larger M' > M is also feasible.
So the predicate feasible(M) is monotone — perfect for binary search.
#include <bits/stdc++.h>
using namespace std;
// Can we allocate so that no student gets > cap pages?
bool feasible(const vector<int>& pages, int k, int cap) {
int students = 1, sum = 0;
for (int p : pages) {
if (p > cap) return false;
if (sum + p > cap) { students++; sum = p; }
else sum += p;
}
return students <= k;
}
int allocateBooks(vector<int>& pages, int k) {
int lo = *max_element(pages.begin(), pages.end());
int hi = accumulate(pages.begin(), pages.end(), 0);
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (feasible(pages, k, mid)) hi = mid;
else lo = mid + 1;
}
return lo;
}
int main() {
vector<int> pages = {12, 34, 67, 90};
cout << "min max pages with 2 students = "
<< allocateBooks(pages, 2) << "\n";
return 0;
}
6. Linear vs Binary — When to Use Which?
Linear Search (কখন)
- Array unsorted
- Very small n (≤ 20)
- Linked list (no random access)
- One-time search, not amortised
Binary Search (কখন)
- Array is sorted
- Many queries on the same data
- Predicate is monotone
- n is large (≥ 1000)
| n | Linear (worst) | Binary (worst) |
|---|---|---|
| 100 | 100 | 7 |
| 1,000 | 1,000 | 10 |
| 1,000,000 | 1,000,000 | 20 |
| 1,000,000,000 | 1 billion | 30 |
7. Off-by-One Bugs & How to Avoid Them
- Define the loop invariant precisely. "answer ∈ [lo, hi]" or "answer ∈ [lo, hi)" — pick one and stick with it.
- Make sure the search range shrinks every iteration. If
lo = midappears, your loop may hang — add a +1 or rethink. - Test boundary cases: empty array, single element, target smaller than all, target larger than all, target equal to first/last.
[lo, hi] না [lo, hi);
(২) প্রতি iteration-এ range যেন ছোট হয় — lo = mid দিলে infinite loop হতে পারে;
(৩) boundary case test করুন — empty, single element, target সবার ছোট/বড়।
8. Practice Problems
Try first, then click to reveal the runnable solution.
-
Find the first occurrence of a target in a sorted array with duplicates.duplicate-সহ sorted array-এ target-এর প্রথম occurrence বের করুন।
✨ Show Answer (উত্তর দেখুন)
first_occ.cpp#include <bits/stdc++.h> using namespace std; int firstOcc(vector<int>& a, int x){ int lo=0, hi=a.size(), ans=-1; while(lo<hi){ int m=lo+(hi-lo)/2; if(a[m]==x){ ans=m; hi=m; } else if(a[m]<x) lo=m+1; else hi=m; } return ans; } int main(){ vector<int> a={1,2,2,2,3,5}; cout << firstOcc(a,2) << "\n"; } -
Find the last occurrence of a target.target-এর সর্বশেষ occurrence বের করুন।
✨ Show Answer (উত্তর দেখুন)
last_occ.cpp#include <bits/stdc++.h> using namespace std; int lastOcc(vector<int>& a, int x){ int lo=0, hi=a.size()-1, ans=-1; while(lo<=hi){ int m=lo+(hi-lo)/2; if(a[m]==x){ ans=m; lo=m+1; } else if(a[m]<x) lo=m+1; else hi=m-1; } return ans; } int main(){ vector<int> a={1,2,2,2,3,5}; cout << lastOcc(a,2) << "\n"; } -
Count occurrences of x in a sorted array in O(log n).sorted array-এ x-এর সংখ্যা O(log n)-এ গণনা করুন।
✨ Show Answer (উত্তর দেখুন)
count.cpp#include <bits/stdc++.h> using namespace std; int main(){ vector<int> a={1,2,2,2,3,5}; int x=2; auto lo = lower_bound(a.begin(),a.end(),x); auto hi = upper_bound(a.begin(),a.end(),x); cout << (hi - lo) << "\n"; } -
Compute integer
sqrt(n)using binary search (nocmath).binary search ব্যবহার করে integersqrt(n)বের করুন।✨ Show Answer (উত্তর দেখুন)
isqrt.cpp#include <bits/stdc++.h> using namespace std; long long isqrt(long long n){ long long lo=0, hi=n, ans=0; while(lo<=hi){ long long m=lo+(hi-lo)/2; if(m*m<=n){ ans=m; lo=m+1; } else hi=m-1; } return ans; } int main(){ cout << isqrt(2025) << "\n"; } -
Find a peak in a mountain array (strictly increases then strictly decreases).mountain array-এ peak খুঁজে বের করুন।
✨ Show Answer (উত্তর দেখুন)
peak.cpp#include <bits/stdc++.h> using namespace std; int peak(vector<int>& a){ int lo=0, hi=a.size()-1; while(lo<hi){ int m=lo+(hi-lo)/2; if(a[m]<a[m+1]) lo=m+1; else hi=m; } return lo; } int main(){ vector<int> a={1,3,7,12,9,4}; cout << peak(a) << " -> " << a[peak(a)] << "\n"; } -
Search in a rotated sorted array (no duplicates).rotated sorted array-এ search করুন।
✨ Show Answer (উত্তর দেখুন)
rotated.cpp#include <bits/stdc++.h> using namespace std; int rotSearch(vector<int>& a, int x){ int lo=0, hi=a.size()-1; while(lo<=hi){ int m=lo+(hi-lo)/2; if(a[m]==x) return m; if(a[lo]<=a[m]){ if(a[lo]<=x && x<a[m]) hi=m-1; else lo=m+1; } else { if(a[m]<x && x<=a[hi]) lo=m+1; else hi=m-1; } } return -1; } int main(){ vector<int> a={7,9,12,1,3,5}; cout << rotSearch(a,3) << "\n"; } -
Painter partition: distribute boards among
kpainters minimizing max paint time.painter partition:kজন painter-এর মধ্যে boards বিতরণ করুন যাতে সর্বাধিক সময় সর্বনিম্ন হয়।✨ Show Answer (উত্তর দেখুন)
painter.cpp#include <bits/stdc++.h> using namespace std; bool ok(vector<int>& b, int k, long long cap){ long long sum=0; int cnt=1; for(int x:b){ if(x>cap) return false; if(sum+x>cap){cnt++;sum=x;} else sum+=x; } return cnt<=k; } int main(){ vector<int> b={10,20,30,40}; int k=2; long long lo=*max_element(b.begin(),b.end()); long long hi=accumulate(b.begin(),b.end(),0LL); while(lo<hi){ long long m=lo+(hi-lo)/2; if(ok(b,k,m)) hi=m; else lo=m+1; } cout << lo << "\n"; } -
Find the smallest element in a rotated sorted array.rotated sorted array-এর সর্বনিম্ন element বের করুন।
✨ Show Answer (উত্তর দেখুন)
min_rot.cpp#include <bits/stdc++.h> using namespace std; int findMin(vector<int>& a){ int lo=0, hi=a.size()-1; while(lo<hi){ int m=lo+(hi-lo)/2; if(a[m]>a[hi]) lo=m+1; else hi=m; } return a[lo]; } int main(){ vector<int> a={15,18,2,3,6,12}; cout << findMin(a) << "\n"; }
Summary — Module 11
Linear search is O(n) and works on any data. Binary search is O(log n) but requires
the data to be sorted (or a monotone predicate). The same halving idea generalises into
binary search on the answer — the trick that solves problems like Allocate Books and Painter Partition.
Master lower_bound and upper_bound — they appear in nearly every contest problem.
lower_bound এবং upper_bound ভালোভাবে শিখে রাখুন —
এগুলো প্রায় সব contest problem-এ লাগে।