Forberedelse til kodeinterviews · Lektion

Klassisk binær søgning: venstre, højre, midt

Implementer binær søgning iterativt og rekursivt, få styr på off-by-one-detaljerne for lo/hi-grænser, og bekræft korrektheden med kanttilfælde.

Lektion 1 af 413 trin

Klassisk binær søgning: venstre, højre, midt er en gratis Forberedelse til kodeinterviews-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Forberedelse til kodeinterviews, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Forberedelse til kodeinterviews-kurset indeholder 4 lektioner i alt.

Hvorfor binær søgning er vigtig

Binær søgning reducerer en lineær gennemgang med O(n) til O(log n) ved at halvere søgerummet i hvert trin. I et array med en million elementer kræver en lineær gennemgang op til 1.000.000 sammenligninger, mens binær søgning højst kræver 20. Denne effektivitet gør den til en af de algoritmer, der oftest testes ved programmeringsjobsamtaler.

Den centrale pointe er, at et sorteret array efter én enkelt sammenligning lader dig afgøre, hvilken halvdel af de resterende data du helt kan forkaste.

Rammen med venstre, midterste og højre grænse

Binær søgning bruger tre indeksmarkører: lo (venstre grænse), hi (højre grænse) og mid (midtpunkt). Ved hver iteration beregner du mid = (lo + hi) // 2 og sammenligner søgeværdien med arr[mid]. Hvis søgeværdien er mindre, flytter du hi = mid - 1; hvis den er større, flytter du lo = mid + 1; hvis de er ens, har du fundet den.

Løkken fortsætter, så længe lo <= hi. Når løkken afsluttes uden at finde søgeværdien, returnerer du -1.

def binary_search(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

print(binary_search([1, 3, 5, 7, 9, 11], 7))  # 3
print(binary_search([1, 3, 5, 7, 9, 11], 6))  # -1

Undgå heltalsoverløb i midtpunktet

Udtrykket mid = (lo + hi) // 2 kan medføre heltalsoverløb i sprog med heltal med fast bredde (Java, C++). Pythons heltal har vilkårlig præcision, så der opstår aldrig overløb, men til en jobsamtale forventes det stadig, at du kender det sikre alternativ: mid = lo + (hi - lo) // 2.

Denne form beregner det samme midtpunkt, men lægger kun halvdelen af afstanden til lo til i stedet for først at lægge begge grænser sammen. Hvis du nævner dette til en jobsamtale, viser det, at du er opmærksom på lavniveauhensyn.

# Safe mid calculation (important in Java/C++, good habit in Python too)
lo, hi = 0, 1_000_000_000
mid_unsafe = (lo + hi) // 2   # fine in Python
mid_safe   = lo + (hi - lo) // 2  # same result, no overflow risk
print(mid_unsafe == mid_safe)  # True

Inklusive og eksklusive grænser

En af de vanskeligste dele ved binær søgning er at vælge, om hi peger på det sidste gyldige indeks (inklusiv, hi = len(arr) - 1) eller på pladsen lige efter slutningen (eksklusiv, hi = len(arr)). Forskellige konventioner kræver forskellige løkkebetingelser og opdateringer af grænserne.

Med inklusive grænser skal du bruge while lo <= hi og opdatere hi = mid - 1. Med eksklusive grænser skal du bruge while lo < hi og opdatere hi = mid. At blande konventionerne er den mest almindelige årsag til fejl i implementeringer af binær søgning.

# Exclusive hi variant — useful for bisect-style lower-bound
def search_exclusive(arr, target):
    lo, hi = 0, len(arr)  # hi is one past last
    while lo < hi:          # strictly less than
        mid = lo + (hi - lo) // 2
        if arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid         # NOT mid - 1
    return lo if lo < len(arr) and arr[lo] == target else -1

print(search_exclusive([2, 4, 6, 8, 10], 6))  # 2

Rekursiv binær søgning

Binær søgning kan skrives rekursivt ved at sende opdaterede grænser for lo og hi gennem kaldestakken. Hvert rekursivt kald halverer søgerummet, så dybden er O(log n). Basistilfældet opstår, når lo > hi (ikke fundet), eller når arr[mid] == target (fundet).

Den iterative version foretrækkes i produktionskode, fordi den undgår ekstra omkostninger til stakrammer, men den rekursive version tydeliggør divide-and-conquer-strukturen bedre på en tavle.

def binary_search_rec(arr, target, lo, hi):
    if lo > hi:
        return -1
    mid = lo + (hi - lo) // 2
    if arr[mid] == target:
        return mid
    elif arr[mid] < target:
        return binary_search_rec(arr, target, mid + 1, hi)
    else:
        return binary_search_rec(arr, target, lo, mid - 1)

arr = [1, 3, 5, 7, 9, 11]
print(binary_search_rec(arr, 9, 0, len(arr) - 1))  # 4

Kanttilfælde: Tomt array, ét element

Robust binær søgning skal kunne håndtere kanttilfælde uden at gå ned. De tre mest almindelige er: et tomt array (løkken udføres aldrig, og -1 returneres korrekt), et array med ét element (mid er lig med lo og hi, så én sammenligning er nok) og søgeværdier uden for området (lo kommer til sidst til at overstige hi, og -1 returneres).

Afprøv altid din implementering med disse inputværdier, før du går videre til opfølgende spørgsmål i en jobsamtale.

def binary_search(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

print(binary_search([], 5))       # -1  (empty)
print(binary_search([7], 7))      # 0   (single, found)
print(binary_search([7], 3))      # -1  (single, not found)
print(binary_search([1,3,5], 0))  # -1  (below range)
print(binary_search([1,3,5], 9))  # -1  (above range)

Tids- og pladskompleksitet

Binær søgning har tidskompleksiteten O(log n), fordi hver sammenligning halverer søgerummet. Efter k sammenligninger er det resterende søgerum n/2^k; søgningen slutter, når dette når 1, så k = log₂ n.

Pladskompleksiteten er O(1) for den iterative version (kun tre heltalsvariabler) og O(log n) for den rekursive version på grund af kaldestakkens dybde. Angiv altid begge dele til en jobsamtale, og foretræk den iterative form, når pladsen er begrænset.

import math

for n in [10, 100, 1000, 1_000_000, 1_000_000_000]:
    steps = math.ceil(math.log2(n + 1))
    print(f'n={n:>12,}  max comparisons={steps}')

Søgning efter eksakt match eller grænse

Klassisk binær søgning returnerer et vilkårligt indeks, hvor søgeværdien findes. Men mange interviewopgaver beder om den første eller sidste forekomst af en søgeværdi. I de tilfælde skal du fortsætte søgningen, selv efter at du har fundet et match — i stedet for at returnere med det samme skal du indsnævre grænsen og fortsætte.

Når du søger efter den første forekomst, skal du efter at have fundet arr[mid] == target gemme mid som en kandidat og sætte hi = mid - 1. For den sidste forekomst skal du sætte lo = mid + 1.

def first_occurrence(arr, target):
    lo, hi, result = 0, len(arr) - 1, -1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if arr[mid] == target:
            result = mid
            hi = mid - 1   # keep searching left
        elif arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return result

print(first_occurrence([1, 2, 2, 2, 3], 2))  # 1

Brug af Pythons bisect-modul

Pythons standardbibliotek indeholder bisect.bisect_left(arr, x) og bisect.bisect_right(arr, x) til binær søgning, der er klar til brug i produktion. bisect_left returnerer det venstre indeks, hvor x kan indsættes, så arrayet forbliver sorteret, hvilket i praksis finder den første position, hvor arr[i] >= x.

Det kan være tilladt at bruge bisect til en jobsamtale; spørg altid først. Det er stadig afgørende at vide, hvordan modulet fungerer bag kulisserne (det er binær søgning med O(log n)).

import bisect

arr = [1, 2, 2, 2, 3, 5]

print(bisect.bisect_left(arr, 2))   # 1  (first 2)
print(bisect.bisect_right(arr, 2))  # 4  (after last 2)

# Check if target exists
target = 3
idx = bisect.bisect_left(arr, target)
print(idx < len(arr) and arr[idx] == target)  # True

Almindelige faldgruber ved binær søgning

Tre fejl står for de fleste fejl i binær søgning til jobsamtaler. For det første en forkert løkkebetingelse: Hvis du bruger < i stedet for <= med inklusive grænser, springer du det sidste resterende element over. For det andet en forkert opdatering af grænsen: Hvis du glemmer +1 eller -1, opstår der en uendelig løkke, når lo == hi. For det tredje at arbejde på et usorteret array: Binær søgning er kun korrekt på sorterede data.

Sig følgende højt, før du skriver binær søgning: 'Arrayet er sorteret, mine grænser er inklusive, og min løkke kører, så længe lo <= hi.'

# BUG: infinite loop when lo == hi because hi = mid never moves past lo
def buggy(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo < hi:              # should be lo <= hi for exact-match
        mid = lo + (hi - lo) // 2
        if arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid            # stops, but never returns mid when found
    return lo if arr[lo] == target else -1

print(buggy([1, 3, 5, 7], 7))  # 3 (works here by luck)
print(buggy([1, 3, 5, 7], 1))  # 0 (correct)
print(buggy([1, 3, 5, 7], 4))  # -1 (correct)

Interviewtips til binær søgning

Når du ser et problem med et sorteret array, en monotont voksende funktion eller et søgerum, der kan halveres, bør du straks overveje binær søgning. Forklar din tankegang til en jobsamtale: 'Fordi arrayet er sorteret, kan jeg forkaste halvdelen af elementerne ved hver sammenligning, hvilket giver O(log n).'

Afprøv altid din løsning med mindst tre inputværdier: en værdi i begyndelsen, en værdi i slutningen og en værdi, der ikke findes. Hvis du på eget initiativ angiver kompleksiteten — 'tid O(log n), plads O(1)' — før du bliver spurgt, viser det et stærkt grundlag.

Hurtigt tjek

Afprøv din forståelse af begreberne fra lektionen Data Structures & Algorithms — Coding Interview Prep.

Opsummering af lektionen

I denne lektion lærte du: binær søgning halverer søgerummet ved hvert trin og giver tidskompleksiteten O(log n), den inklusive grænsekonvention bruger lo <= hi med opdateringerne lo = mid+1 og hi = mid-1, og for at finde den første eller sidste forekomst fortsætter du søgningen efter et match i stedet for at returnere med det samme. Næste gang undersøger vi, hvordan binær søgning udvides til roterede og usorterede arrays.

Gratis at komme i gang

Lær Forberedelse til kodeinterviews med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
90
Lektioner
360

Ofte stillede spørgsmål

Er lektionen “Klassisk binær søgning: venstre, højre, midt” gratis?

Ja — hele teksten til “Klassisk binær søgning: venstre, højre, midt” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Forberedelse til kodeinterviews-kurset, skal du opgradere til CoddyKit PRO. Forberedelse til kodeinterviews-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Klassisk binær søgning: venstre, højre, midt”?

Implementer binær søgning iterativt og rekursivt, få styr på off-by-one-detaljerne for lo/hi-grænser, og bekræft korrektheden med kanttilfælde. Du øver dig i Forberedelse til kodeinterviews med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Forberedelse til kodeinterviews?

Der kræves ingen tidligere erfaring. Forberedelse til kodeinterviews på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.

Hvor lang tid tager lektionen “Klassisk binær søgning: venstre, højre, midt”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Forberedelse til kodeinterviews-lektion?

Ja. Alle Forberedelse til kodeinterviews-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Klassisk binær søgning: venstre, højre, midt
  2. Binær søgning i roterede og usorterede arrays
  3. Nedre og øvre grænse
  4. Binær søgning i svarområdet
← Tilbage til Forberedelse til kodeinterviews