Specimen 02 ยท O(log n) searching

Binary Search

On a sorted row, look at the middle value. Too small, drop the left half. Too big, drop the right half. Repeat โ€” the search space halves every step.

โ† Back to the Field Guide
02-namuna ยท O(log n) qidiruv

Binary Search

Saralangan qatorda o'rtadagi qiymatga qarang. Kichik bo'lsa โ€” chap yarmini tashlang. Katta bo'lsa โ€” o'ng yarmini tashlang. Har qadamda qidiruv maydoni ikkiga bo'linadi.

โ† Qo'llanmaga qaytish

Intuition

Watch the bars below: each round checks the middle of the current range and throws away the half that can't contain the target. Do that enough times and a range of thousands shrinks to one candidate in about twenty comparisons โ€” but only because the array is sorted.

Tushuncha (Intuition)

Quyidagi ustunlarga qarang: har bosqich joriy diapazonning o'rtasini tekshiradi va target bo'lishi mumkin bo'lmagan yarmini tashlab yuboradi. Buni yetarlicha takrorlasangiz, minglab elementli diapazon atigi yigirmaga yaqin solishtirishda bitta nomzodgacha qisqaradi โ€” lekin bu faqat array saralangan bo'lgani uchun ishlaydi.

How It Works

  1. Set two boundaries: lo = 0 and hi = len(arr) - 1, marking the entire array as the current search range.
  2. While lo <= hi, the range still has at least one candidate left, so compute the middle index: mid = (lo + hi) // 2 (or lo + (hi - lo) // 2 to dodge overflow โ€” more on that below).
  3. Compare arr[mid] to the target. If they're equal, you've found it โ€” return mid right away.
  4. If arr[mid] is less than target, the target can only be to the right (the array is sorted, so everything at or before mid is too small) โ€” move lo = mid + 1, discarding the left half and the middle.
  5. If arr[mid] is greater than target, the target can only be to the left โ€” move hi = mid - 1, discarding the right half and the middle.
  6. Go back to step 2 with the new, smaller [lo, hi] range. Each round the range is roughly half the size of the round before.
  7. If lo ever ends up greater than hi, the range has shrunk to nothing โ€” the target isn't in the array. Return -1.

Qanday Ishlaydi

  1. Ikkita chegara belgilang: lo = 0 va hi = len(arr) - 1 โ€” bu butun array'ni joriy qidiruv maydoni sifatida belgilaydi.
  2. lo <= hi bo'lar ekan, maydonda hali kamida bitta nomzod bor demakdir โ€” shuning uchun o'rta indeksni hisoblang: mid = (lo + hi) // 2 (yoki overflow'dan qochish uchun lo + (hi - lo) // 2 โ€” bu haqda pastda batafsil).
  3. arr[mid] ni target bilan solishtiring. Agar teng bo'lsa โ€” topdingiz, darhol mid ni qaytaring.
  4. Agar arr[mid] targetdan kichik bo'lsa, target faqat o'ngda bo'lishi mumkin (array saralangan, shuning uchun mid va undan oldingi hamma narsa juda kichik) โ€” lo = mid + 1 qiling, chap yarmi va o'rtadagi elementni tashlab yuboring.
  5. Agar arr[mid] targetdan katta bo'lsa, target faqat chapda bo'lishi mumkin โ€” hi = mid - 1 qiling, o'ng yarmi va o'rtadagi elementni tashlab yuboring.
  6. Yangi, kichikroq [lo, hi] maydoni bilan 2-qadamga qayting. Har bosqichda maydon avvalgisining taxminan yarmiga teng bo'ladi.
  7. Agar lo biror payt hidan katta bo'lib qolsa, maydon butunlay tugagan โ€” target array'da yo'q. -1 qaytaring.

Complexity

Time: O(log n). Every comparison throws away half of whatever candidates were still left, so after k comparisons only about n / 2k elements remain in play. The search ends once that count hits zero (or one), which takes about logโ‚‚(n) comparisons โ€” for a million-element array that's roughly 20 steps instead of up to a million for a plain linear scan. In the best case, the very first middle element you check happens to be the target, giving O(1).

Space: O(1). The iterative version shown here only ever tracks three numbers โ€” lo, hi, and mid โ€” no matter how large the array is; it never builds a second array or any structure that grows with the input. (A recursive version would spend O(log n) space on the call stack instead, since each recursive call waits on the stack for the one below it to return.)

Murakkablik

Vaqt: O(log n). Har bir solishtirish qolgan nomzodlarning yarmini yo'q qiladi, shuning uchun k ta solishtirishdan keyin taxminan n / 2k ta element qoladi. Qidiruv bu son nolga (yoki bittaga) tushganda tugaydi โ€” bu taxminan logโ‚‚(n) ta solishtirishni talab qiladi. Million elementli array uchun bu oddiy chiziqli qidiruvdagi millionga yaqin qadam o'rniga atigi 20 ga yaqin qadam degani. Eng yaxshi holatda, tekshirilgan birinchi o'rta element aynan target bo'lib chiqsa, O(1) hosil bo'ladi.

Xotira: O(1). Shu yerda ko'rsatilgan iterativ versiya array qanchalik katta bo'lishidan qat'i nazar faqat uchta sonni โ€” lo, hi va midni โ€” kuzatib boradi; u hech qachon ikkinchi array yoki kirish hajmiga qarab o'sadigan boshqa struktura yaratmaydi. (Rekursiv versiya esa call stack uchun O(log n) xotira sarflagan bo'lardi, chunki har bir rekursiv chaqiruv o'zidan keyingisi qaytishini stack'da kutib turadi.)

Common Mistakes

  • Using lo < hi instead of lo <= hi as the loop condition. Fix: use <= โ€” with <, the moment exactly one candidate is left (lo == hi), the loop exits without ever checking it.
  • Writing mid = (lo + hi) / 2 in a language like C++ or Java, where lo and hi are fixed-width 32-bit integers: if the array is large enough that lo + hi exceeds about 2.1 billion, the addition overflows and wraps around to a negative number, producing a garbage mid. Fix: compute mid = lo + (hi - lo) / 2 instead โ€” the intermediate value never exceeds hi, so it can't overflow. Python doesn't have this problem at all: its integers have arbitrary precision and grow automatically as needed, so lo + hi can never overflow no matter how large the numbers get โ€” but writing lo + (hi - lo) // 2 is still a fine habit if you ever port the code to C++ or Java.
  • Narrowing the range to hi = mid or lo = mid instead of mid - 1 / mid + 1. Fix: always exclude mid itself once it's been checked and ruled out โ€” leaving it in the range risks comparing the same element forever and looping infinitely.
  • Running binary search on data that isn't actually sorted. It won't crash โ€” it'll just silently return the wrong index or -1 for a value that's really there, because the "discard half" logic assumes an order that doesn't exist. Fix: sort the data first, or reach for a different algorithm if sorting isn't an option.

Ko'p Uchraydigan Xatolar

  • Loop shartini lo <= hi o'rniga lo < hi deb yozish. Yechim: <= dan foydalaning โ€” < bilan, aynan bitta nomzod qolganda (lo == hi), loop uni umuman tekshirmasdan tugaydi.
  • C++ yoki Java kabi tilda mid = (lo + hi) / 2 deb yozish โ€” bu yerda lo va hi qat'iy 32-bitli butun sonlar: agar array yetarlicha katta bo'lib, lo + hi taxminan 2.1 milliarddan oshsa, qo'shish overflow bo'ladi va manfiy songa aylanib, noto'g'ri mid beradi. Yechim: buning o'rniga mid = lo + (hi - lo) / 2 ni hisoblang โ€” bu oraliq qiymat hech qachon hidan oshmaydi, shuning uchun overflow bo'lolmaydi. Python'da bu muammo umuman yo'q: uning butun sonlari arbitrary-precision โ€” ular kerak bo'lganda avtomatik o'sadi, shuning uchun lo + hi qanchalik katta bo'lishidan qat'i nazar hech qachon overflow bo'lmaydi โ€” lekin kodni C++ yoki Java'ga ko'chirsangiz, lo + (hi - lo) // 2 deb yozish baribir yaxshi odat.
  • Maydonni mid - 1 / mid + 1 o'rniga hi = mid yoki lo = mid deb qisqartirish. Yechim: tekshirilib, rad etilgan midni har doim maydondan chiqarib tashlang โ€” uni qoldirish xuddi shu elementni cheksiz solishtirish va cheksiz loop xavfini tug'diradi.
  • Binary search'ni haqiqatda saralanmagan ma'lumotda ishlatish. Bu xato bermaydi โ€” shunchaki noto'g'ri index yoki haqiqatda mavjud bo'lgan qiymat uchun -1 qaytaradi, chunki "yarmini tashlash" mantig'i mavjud bo'lmagan tartibni taxmin qiladi. Yechim: avval ma'lumotni saralang, yoki saralash imkoni bo'lmasa boshqa algoritmga murojaat qiling.

When to Use It

  • The data is already sorted, or gets searched often enough that sorting it once up front pays for itself โ€” binary search's entire trick depends on that order.
  • You need more than an exact match โ€” e.g. the first index where a condition flips from false to true (insert position, first/last occurrence of a value) โ€” the same halving logic works on these "boundary" questions, often called binary search on the answer.
  • The data lives somewhere with O(1) random access, like an array โ€” jumping straight to index mid has to be cheap, or the whole speed advantage disappears (a linked list, where reaching the middle costs O(n) itself, is a bad fit).
  • Skip it when the data changes constantly and re-sorting would cost more than it saves, or when you just need a single lookup by key โ€” a hash map answers that in O(1) without needing any order at all.

Qachon Ishlatish Kerak

  • Ma'lumot allaqachon saralangan, yoki yetarlicha tez-tez qidiriladiki, uni bir marta oldindan saralash o'zini oqlaydi โ€” binary search'ning butun hiylasi shu tartibga bog'liq.
  • Sizga faqat aniq moslikdan ko'proq narsa kerak โ€” masalan, shart false'dan true'ga o'zgaradigan birinchi index (insert position, qiymatning birinchi/oxirgi uchrashi) โ€” xuddi shu yarmiga bo'lish mantig'i bu "chegara" savollarida ham ishlaydi, ko'pincha "binary search on the answer" deb ataladi.
  • Ma'lumot O(1) tasodifiy kirish (random access) mumkin bo'lgan joyda saqlanadi, masalan array โ€” mid indeksiga to'g'ridan-to'g'ri sakrash arzon bo'lishi kerak, aks holda butun tezlik ustunligi yo'qoladi (linked list, unda o'rtaga yetib borish o'zi O(n) turadi, bu yerga mos kelmaydi).
  • Ma'lumot doimiy o'zgarib turadigan va qayta saralash tejaganidan ko'proq xarajat qiladigan hollarda, yoki sizga faqat kalit bo'yicha bitta lookup kerak bo'lsa, undan saqlaning โ€” hash map hech qanday tartibsiz ham O(1) da javob beradi.

LeetCode Practice

704. Binary Search โ†—

Restated: given an integer array nums sorted in ascending order and an integer target, return the index of target in nums if it exists, or -1 if it doesn't โ€” and do it in O(log n) time.

  1. Notice the constraint first. "O(log n) time" isn't a suggestion โ€” it rules out scanning the array one element at a time and points directly at halving the search range, i.e. binary search.
  2. Set up the initial range. lo, hi = 0, len(nums) - 1 covers the whole array โ€” every index is still a candidate before the first comparison.
  3. Loop while a candidate could still exist. while lo <= hi: โ€” compute mid, then compare nums[mid] against target.
  4. Branch on the comparison, exactly as worked out above. Equal โ†’ return mid. Too small โ†’ lo = mid + 1. Too big โ†’ hi = mid - 1. Each branch keeps the sorted-order guarantee intact for whatever range is left.
  5. Handle the "not found" exit. If the loop condition ever fails, lo has crossed past hi, meaning every candidate has been eliminated โ€” return -1.
def search(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2  # avoids the C++/Java overflow trap (harmless in Python too)
        if nums[mid] == target:
            return mid              # found it, return the index
        elif nums[mid] < target:
            lo = mid + 1            # target must be to the right, discard mid and everything left of it
        else:
            hi = mid - 1            # target must be to the left, discard mid and everything right of it
    return -1                       # lo > hi: range emptied out, target isn't in nums

LeetCode Amaliyoti

704. Binary Search โ†—

Qayta bayon: o'sish tartibida saralangan butun sonlar array'i nums va butun son target berilgan โ€” agar target numsda mavjud bo'lsa uning indeksini, aks holda -1 ni qaytaring, va buni O(log n) vaqtda bajaring.

  1. Avval cheklovga e'tibor bering. "O(log n) vaqt" shunchaki tavsiya emas โ€” bu array'ni birma-bir skanerlashni istisno qiladi va to'g'ridan-to'g'ri qidiruv maydonini ikkiga bo'lishga, ya'ni binary search'ga ishora qiladi.
  2. Boshlang'ich maydonni o'rnating. lo, hi = 0, len(nums) - 1 butun array'ni qamrab oladi โ€” birinchi solishtirishdan oldin har bir index hali nomzod hisoblanadi.
  3. Nomzod mavjud bo'lishi mumkin ekan, loop qiling. while lo <= hi: โ€” midni hisoblang, so'ng nums[mid] ni target bilan solishtiring.
  4. Solishtirish natijasiga qarab tarmoqlaning โ€” xuddi yuqorida ko'rib chiqilganidek. Teng โ†’ midni qaytaring. Juda kichik โ†’ lo = mid + 1. Juda katta โ†’ hi = mid - 1. Har bir tarmoq qolgan maydon uchun saralangan tartib kafolatini saqlab qoladi.
  5. "Topilmadi" holatini boshqaring. Agar loop sharti biror payt bajarilmasa, lo allaqachon hidan oshib ketgan bo'ladi โ€” bu barcha nomzodlar yo'qqa chiqarilganini bildiradi โ€” -1 qaytaring.
def search(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2  # avoids the C++/Java overflow trap (harmless in Python too)
        if nums[mid] == target:
            return mid              # found it, return the index
        elif nums[mid] < target:
            lo = mid + 1            # target must be to the right, discard mid and everything left of it
        else:
            hi = mid - 1            # target must be to the left, discard mid and everything right of it
    return -1                       # lo > hi: range emptied out, target isn't in nums

Check Yourself

O'zingizni Sinang

ยฉ 2026 Davronbek