Skip to content
DSA searching 12 min read

Searching Exercises: Answer Sheet & Worked Solutions

This is the answer sheet for the searching exercises. Each solution gives the key idea, the optimal complexity, the code in all four languages (use the switcher), and common pitfalls. Try the problems first — then check your work here.

Idea: Track an inclusive range [lo, hi]. Test the midpoint; if it’s too small move lo right, if too big move hi left. Each step halves the range. Use lo + (hi - lo) / 2 so the midpoint can’t overflow.

Complexity: O(log n) time, O(1) space.

int binarySearch(const std::vector<int>& a, int target) {
    int lo = 0, hi = (int)a.size() - 1;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;
        if (a[mid] == target) return mid;
        else if (a[mid] < target) lo = mid + 1;
        else hi = mid - 1;
    }
    return -1;
}

Pitfall: With an inclusive range you must use lo <= hi and move bounds to mid ± 1. Using lo < hi misses a final single-element range; setting hi = mid (not mid - 1) can loop forever.

Q2. Search insert position

Idea: This is exactly lower_bound: the first index whose element is >= target. Use the half-open [lo, hi) template and return lo — which equals the insertion point even when target is absent.

Complexity: O(log n) time, O(1) space.

int searchInsert(const std::vector<int>& a, int target) {
    int lo = 0, hi = (int)a.size();   // half-open [lo, hi)
    while (lo < hi) {
        int mid = lo + (hi - lo) / 2;
        if (a[mid] < target) lo = mid + 1;
        else hi = mid;
    }
    return lo;
}

Pitfall: Initialize hi = n (one past the end), not n - 1. If target is larger than every element, the answer is n, and that’s only reachable when hi can reach n.

Q3. Count occurrences of a value

Idea: Count = upperBound(target) - lowerBound(target). lowerBound is the first index >= target; upperBound is the first index > target. Their gap is exactly how many equal target. No need to find each copy.

Complexity: O(log n) time, O(1) space.

int lowerBound(const std::vector<int>& a, int t) {
    int lo = 0, hi = (int)a.size();
    while (lo < hi) { int m = lo + (hi - lo) / 2; if (a[m] < t) lo = m + 1; else hi = m; }
    return lo;
}
int upperBound(const std::vector<int>& a, int t) {
    int lo = 0, hi = (int)a.size();
    while (lo < hi) { int m = lo + (hi - lo) / 2; if (a[m] <= t) lo = m + 1; else hi = m; }
    return lo;
}
int countOccurrences(const std::vector<int>& a, int target) {
    return upperBound(a, target) - lowerBound(a, target);
}

Pitfall: The only difference between the two bounds is < vs <=. Swap them by accident and you’ll get a count of 0 or a negative number. (Python’s bisect.bisect_right - bisect.bisect_left does the same thing.)

Q4. Two Sum II (sorted input)

Idea: Two pointers from opposite ends. If the pair sum is too small, move lo right for a larger value; if too big, move hi left. Each step provably discards one candidate, so it’s a single linear sweep — no extra memory, unlike the hash-map version for unsorted input.

Complexity: O(n) time, O(1) space.

std::pair<int,int> twoSumSorted(const std::vector<int>& a, int target) {
    int lo = 0, hi = (int)a.size() - 1;
    while (lo < hi) {
        int s = a[lo] + a[hi];
        if (s == target) return {lo, hi};
        else if (s < target) lo++;
        else hi--;
    }
    return {-1, -1};
}

Pitfall: Two pointers only works because the array is sorted. On unsorted input you can’t tell which pointer to move — use a hash map instead.

Q5. Integer square root

Idea: Binary-search the answer in [0, n] for the largest k with k*k <= n. Keep the best feasible k as you go. Compare with 64-bit math so mid*mid doesn’t overflow.

Complexity: O(log n) time, O(1) space.

int isqrt(int n) {
    long long lo = 0, hi = n, ans = 0;
    while (lo <= hi) {
        long long mid = lo + (hi - lo) / 2;
        if (mid * mid <= (long long)n) { ans = mid; lo = mid + 1; }
        else hi = mid - 1;
    }
    return (int)ans;
}

Pitfall: In C++/Java, mid * mid overflows 32-bit int for large n. Do the multiply in long/long long (as above). JavaScript and Python integers don’t have this issue.

Q6. Find minimum in a rotated sorted array

Idea: Binary search comparing a[mid] to the right end a[hi]. If a[mid] > a[hi], the rotation point (and hence the minimum) is to the right of mid, so lo = mid + 1. Otherwise the minimum is at mid or left, so hi = mid. The range collapses onto the minimum.

Complexity: O(log n) time, O(1) space.

int findMin(const std::vector<int>& a) {
    int lo = 0, hi = (int)a.size() - 1;
    while (lo < hi) {
        int mid = lo + (hi - lo) / 2;
        if (a[mid] > a[hi]) lo = mid + 1;   // min is to the right
        else hi = mid;                       // min is mid or to the left
    }
    return a[lo];
}

Pitfall: Compare to a[hi], not a[lo]. Comparing to the left end is ambiguous when the array isn’t rotated. Also note this assumes distinct values — duplicates (e.g. [3,3,1,3]) break the strict comparison and need an extra hi-- fallback.

Q7. Capacity to ship packages within D days

Idea: Binary search on the answer. Capacity must be at least the heaviest package (max) and at most all packages in one day (sum). feasible(cap) greedily counts days: keep loading until the next package would overflow cap, then start a new day. Search the smallest cap whose day count is <= days — feasibility is monotonic in cap.

Complexity: O(n · log(sum)) time, O(1) space.

int daysNeeded(const std::vector<int>& w, int cap) {
    int days = 1, cur = 0;
    for (int x : w) {
        if (cur + x > cap) { days++; cur = 0; }
        cur += x;
    }
    return days;
}
int shipWithinDays(const std::vector<int>& w, int days) {
    int lo = 0, hi = 0;
    for (int x : w) { lo = std::max(lo, x); hi += x; }  // [max, sum]
    while (lo < hi) {
        int mid = lo + (hi - lo) / 2;
        if (daysNeeded(w, mid) <= days) hi = mid;       // feasible, try smaller
        else lo = mid + 1;                              // too small, grow
    }
    return lo;
}

Pitfall: The lower bound must be max(weights), not 1 — a capacity below the heaviest package can never ship it, and your greedy check would loop forever or undercount. Always set the answer range to genuinely possible values.


That’s the full answer sheet. Back to the searching exercises or continue to sliding window.

Last updated June 25, 2026
Was this helpful?