Bölüm 3: Arama Algoritmaları

Algoritmalar
Arama problemiDoğrusal aramaİkili aramaHash ile arama Ara değer aramaAtlayarak aramaÜstel aramaKarşılaştırma
Sercan KÜLCÜ · Giresun Üniversitesi · Bilgisayar Mühendisliği

Giriş

Öğrenme Çıktıları

Temel aramalar

Doğrusal ve ikili aramayı Java ile yazmak, bir dizi üzerinde adım adım izlemek; en iyi, ortalama ve en kötü durumlarını hesaplamak.

Hash ile arama

Hash fonksiyonu, kova, çakışma, doğrusal yoklama ve zincirleme kavramlarını açıklamak; yük faktörünün maliyete etkisini yorumlamak.

Sıralı dizide gelişmiş aramalar

Ara değer, atlayarak ve üstel aramanın fikrini, kodunu ve O(log log n), O(√n), O(log i) karmaşıklıklarını gerekçelendirmek.

Seçim

Verinin sıralı olup olmamasına, dağılımına ve boyutuna göre uygun arama algoritmasını seçmek; yaygın uygulama hatalarından kaçınmak.

Giriş

Arama Problemi

  • Arama algoritmaları, bir veri kümesinde istenen değeri bulmak için kullanılır.
  • Verileri bir arama ölçütüne (çoğunlukla bir anahtar, key) göre tarar ve eşleşen öğeyi bulmaya çalışır.
Girdin elemanlı bir dizi ve bir aranan değer.
Çıktıdizi[i] == aranan olan bir indis i; aranan yoksa −1 (başarısız arama).
Maliyet ölçüsü: aramalarda süre, genellikle aranan değerin dizideki elemanlarla kaç kez karşılaştırıldığı (yoklama, probe sayısı) ile ölçülür. Bu bölümdeki tüm karmaşıklıklar bu sayıyı verir.

Giriş

Arama Algoritmalarının Çeşitleri

Farklı arama algoritmaları, farklı çalışma prensiplerine ve önkoşullara sahiptir.

AlgoritmaÖnkoşulÇalışma prensibi
Doğrusal (Linear Search)yokVerileri tek tek karşılaştırarak arar.
İkili (Binary Search)sıralı diziVeri kümesini ikiye bölerek arama alanını daraltır.
Hash (Hash Search)hash tablosuVeriler bir hash tablosuna yerleştirilir; arama, anahtarın kovasında yapılır.
Ara değer (Interpolation)sıralı, düzgün dağılımlıKonumu, değerlerin oranından tahmin eder.
Atlayarak (Jump Search)sıralı dizi√n büyüklüğünde bloklar atlar, sonra blok içinde doğrusal arar.
Üstel (Exponential)sıralı diziSınırı 1, 2, 4, 8, … diye büyütür, sonra ikili arama yapar.

Doğrusal Arama

Doğrusal Arama (Linear Search)

  • Listedeki her öğeyi tek tek kontrol ederek arama yapar.
  • Alışveriş listesindeki ürünleri sırayla kontrol etmeye ya da kayıp bir eşyayı bulmak için odayı sistematik olarak taramaya benzer.
  • Aranan değer, listenin başından başlanarak her öğe ile karşılaştırılır:
    • bulunursa konumu (indisi) döndürülür;
    • listede yoksa başarısız sonuç −1 döndürülür.
  • Bazen aranan bilgiyi bulmak için tek tek bakmak en iyi yoldur: dizi sırasızsa başka seçenek yoktur.
0331212123748↑ i12 = 12?

Doğrusal Arama

Doğrusal Arama Algoritması

int dogrusalArama(int[] dizi, int n,
                  int aranan) {
  int i = 0;
  while (i < n && dizi[i] != aranan)
    i++;
  if (i < n)
    return i;      // bulundu: indis
  else
    return -1;     // bulunamadı
}
  • Parametreler: dizi aranacak öğeler, n dizinin boyutu, aranan aranacak öğe.
  • Dönüş: aranan öğenin indisi; bulunamazsa −1.
  • i dizideki öğeleri dolaşır; while döngüsü i < n ve dizi[i] ≠ aranan olduğu sürece devam eder, her turda i bir artar.
  • Döngüden sonra if, öğenin bulunup bulunmadığını denetler: i < n ise i (öğenin indisi), değilse −1 döner.
Koşulların sırası önemlidir: i < n önce sınanır; Java'nın kısa devre && işleci sayesinde i == n olduğunda dizi[n]'e hiç erişilmez.

Doğrusal Arama · Adım adım

Doğrusal Arama: En İyi ve En Kötü Durum

03312121237485163744856918

En iyi durum: 33 aranır. Dizi sırasızdır: 33, 21, 12, 7, 8, 1, 3, 44, 56, 18 (n = 10).

03312121237485163744856918↑ i = 0

dizi[0] = 33 = aranan → döngü hemen biter; 0 döner. Yalnızca 1 karşılaştırma: en iyi durum O(1).

03312121237485163744856918

En kötü durum: 17 aranır. 17 dizide yoktur; her elemana bakmak gerekir.

03312121237485163744856918↑ i = 0

i = 0: dizi[0] = 33 ≠ 17 → i = 1.

03312121237485163744856918↑ i = 1

i = 1: dizi[1] = 21 ≠ 17 → i = 2.

03312121237485163744856918↑ i = 2

i = 2: dizi[2] = 12 ≠ 17 → i = 3.

03312121237485163744856918↑ i = 3

i = 3: dizi[3] = 7 ≠ 17 → i = 4.

03312121237485163744856918↑ i = 4

i = 4: dizi[4] = 8 ≠ 17 → i = 5.

03312121237485163744856918↑ i = 5

i = 5: dizi[5] = 1 ≠ 17 → i = 6.

03312121237485163744856918↑ i = 6

i = 6: dizi[6] = 3 ≠ 17 → i = 7.

03312121237485163744856918↑ i = 7

i = 7: dizi[7] = 44 ≠ 17 → i = 8.

03312121237485163744856918↑ i = 8

i = 8: dizi[8] = 56 ≠ 17 → i = 9.

03312121237485163744856918↑ i = 9

i = 9: dizi[9] = 18 ≠ 17 → i = 10.

03312121237485163744856918

i = 10 = n → döngü biter; i < n yanlış, −1 (bulunamadı) döner. n = 10 karşılaştırma: en kötü durum O(n).

Doğrusal Arama · Kodu adım adım çalıştır

dogrusalArama(dizi, 10, 1): Kodu Adım Adım İzle

03312121237485163744856918↑ i

i = 0 ile başlanır. Aranan: 1 (ortalama durum örneği).

03312121237485163744856918↑ i

i < n ve dizi[0] = 33 ≠ 1 → döngü gövdesi çalışır.

03312121237485163744856918↑ i

i++ → i = 1.

03312121237485163744856918↑ i

i < n ve dizi[1] = 21 ≠ 1 → döngü gövdesi çalışır.

03312121237485163744856918↑ i

i++ → i = 2.

03312121237485163744856918↑ i

i < n ve dizi[2] = 12 ≠ 1 → döngü gövdesi çalışır.

03312121237485163744856918↑ i

i++ → i = 3.

03312121237485163744856918↑ i

i < n ve dizi[3] = 7 ≠ 1 → döngü gövdesi çalışır.

03312121237485163744856918↑ i

i++ → i = 4.

03312121237485163744856918↑ i

i < n ve dizi[4] = 8 ≠ 1 → döngü gövdesi çalışır.

03312121237485163744856918↑ i

i++ → i = 5.

03312121237485163744856918↑ i

dizi[5] = 1 = aranan → while koşulu yanlış, döngüden çıkılır.

03312121237485163744856918↑ i

i = 5 < n → 5 döner. 6 karşılaştırma yapıldı.

int dogrusalArama(int[] dizi, int n,
                  int aranan) {
  int i = 0;
  while (i < n && dizi[i] != aranan)
    i++;
  if (i < n)
    return i;
  else
    return -1;
}

Doğrusal Arama

Doğrusal Arama: Karmaşıklık ve Değerlendirme

DurumKarşılaştırmaMertebeÖrnek (n = 10)
En iyi (ilk eleman)1O(1)33 → 1 karşılaştırma
Ortalama (başarılı, eşit olasılık)(1 + 2 + … + n) / n = (n + 1)/2O(n)ortalama 5,5
En kötü / başarısıznO(n)17 → 10 karşılaştırma

Avantajlar

  • Kodlaması ve anlaşılması kolay; karmaşık veri yapısı gerektirmez.
  • Ön hazırlık yoktur: dizinin sıralı olması gerekmez.
  • Küçük veri kümelerinde ideal (ör. rehber listesinde bir numara).
  • Bağlı liste gibi yalnızca sıralı erişilen yapılarda da çalışır.

Dezavantajlar

  • Büyük veride yavaş: 10⁶ elemanda ortalama 500 000 karşılaştırma.
  • Dizi sıralı olsa bile bu bilgiden yararlanmaz.
  • Çok sayıda sorgu varsa diziyi bir kez sıralayıp ikili arama yapmak daha ucuzdur.

Doğrusal Arama

Gözcülü (Sentinel) Doğrusal Arama

int gozcuIleAra(int[] dizi, int n,
                int aranan) {
  int sonEleman = dizi[n - 1];
  dizi[n - 1] = aranan;     // gözcü
  int i = 0;
  while (dizi[i] != aranan)
    i++;
  dizi[n - 1] = sonEleman;  // geri koy
  if (i < n - 1 || sonEleman == aranan)
    return i;
  return -1;
}
  • Klasik döngü her turda iki karşılaştırma yapar: i < n ve dizi[i] != aranan.
  • Aranan değer son hücreye gözcü (sentinel) olarak yazılırsa döngü mutlaka durur; i < n testine gerek kalmaz.
  • Karşılaştırma sayısı ≈ 2n'den ≈ n + 2'ye iner; mertebe yine O(n)'dir.
Son eleman geçici olarak değiştirildiği için dizi salt okunur olmamalı ve aynı anda başka bir iş parçacığı tarafından okunmamalıdır.

İkili Arama

İkili Arama (Binary Search)

  • Kütüphanede kitap aramaya benzer: kitaplar yazarın soyadına göre alfabetik sıralanmıştır.
  • Tüm rafları tek tek aramak yerine önce orta bölüme gidilir.
    • Kitap ortanın solundaysa sol taraftaki raflarda,
    • sağındaysa sağ taraftaki raflarda aramaya devam edilir.
  • Her karşılaştırma, aranması gereken kısmı yarıya indirir.
  • Önkoşul: dizi sıralı olmalıdır ve elemanlara indisle O(1)'de erişilebilmelidir.
0517210317423534655767899↑ orta23 < 54↑ bas↑ son

İkili Arama

İkili Arama Neden Hızlı?

  • Sıralı listelerde arama yaparken hızlıdır: her adımda kalan kısım yarıya iner.
  • 1000 öğeli bir listede doğrusal arama ortalama 500 kontrol yaparken ikili arama en fazla 10 adımda aramayı tamamlar.
  • 1000 → 500 → 250 → 125 → 62 → 31 → 15 → 7 → 3 → 1: her aralık boyu bir karşılaştırma, toplam 10.
  • Büyük ve sıralı veri kümelerinde arama için idealdir.
nDoğrusal (en kötü)İkili (en kötü)
10104
1 0001 00010
10⁶10⁶20
10⁹10⁹30
İkili arama en fazla ⌊log₂ n⌋ + 1 karşılaştırma yapar. Veri 1000 kat büyüdüğünde yalnızca ≈ 10 adım eklenir.

İkili Arama

İkili Arama Algoritması

int ikiliArama(int[] dizi, int n, int aranan) {
  int bas = 0, son = n - 1;
  while (bas <= son) {
    int orta = (bas + son) / 2;
    if (aranan < dizi[orta])
      son = orta - 1;      // sol yarı
    else if (aranan > dizi[orta])
      bas = orta + 1;      // sağ yarı
    else
      return orta;         // bulundu
  }
  return -1;
}
  • bas ve son, arama yapılacak aralığın alt ve üst sınırlarıdır.
  • while, bas ≤ son olduğu (aralık boş olmadığı) sürece devam eder.
  • orta, her adımda aralığın ortasındaki öğenin indisidir.
  • Aranan ortadan küçükse alt yarıda, büyükse üst yarıda devam edilir; eşitse orta döndürülür.
  • Aralık boşalırsa (bas > son) −1 döner.

İkili Arama · Adım adım

İkili Arama = Dengeli İkili Arama Ağacı

51112141518192123252728303237

Sıralı dizi: 15 eleman. Bu diziden en iyi (dengeli) ikili arama ağacını kuralım.

51112141518192123252728303237

Ortadaki eleman 21 kök olur: ikili aramanın ilk karşılaştırması daima budur.

küçükbüyük51112141518192123252728303237

Sol yarının ortası 14, sağ yarının ortası 28: 21'den küçükler solda, büyükler sağda.

küçükbüyük51112141518192123252728303237

Her alt aralığın ortası bir sonraki düzeyi oluşturur: 11, 18, 25, 32.

küçükbüyük51112141518192123252728303237

Kalan elemanlar yapraklardır. Ağacın yüksekliği 3, düzey sayısı 4 = ⌊log₂ 15⌋ + 1: her arama en fazla 4 karşılaştırma yapar.

İkili Arama · Adım adım

Sıralı Dizi Örtük Bir Ağaçtır

135678111416182022232527

İkinci bir sıralı dizi (1, 3, 5, …, 27) de aynı şekilde ağaç olarak görülebilir. Ağaç bellekte kurulmaz: orta hesabı onu örtük olarak gezer. 20'yi arayalım.

135678111416182022232527

orta = 7 → dizi[7] = 14; 20 > 14 → sağa.

135678111416182022232527

orta = 11 → dizi[11] = 22; 20 < 22 → sola.

135678111416182022232527

orta = 9 → dizi[9] = 18; 20 > 18 → sağa.

135678111416182022232527

orta = 10 → dizi[10] = 20: bulundu. Kökten yaprağa 4 karşılaştırma; yol 14 → 22 → 18 → 20.

İkili Arama · Adım adım

İkili Arama: En İyi ve En Kötü Durum

0517210317423534655767899↑ bas↑ son

En iyi durum: 23 aranır. bas = 0, son = 8.

0517210317423534655767899↑ orta↑ bas↑ son

orta = (0 + 8) / 2 = 4 → dizi[4] = 23 = aranan: 1 adımda bulundu, 4 döner.

0517210317423534655767899↑ bas↑ son

En kötü durum: 54 aranır (dizide yok).

0517210317423534655767899↑ orta↑ bas↑ son

Adım 1: orta = 4 → 23 < 54 → bas = 5; sol yarı elenir.

0517210317423534655767899↑ orta↑ bas↑ son

Adım 2: orta = (5 + 8) / 2 = 6 → 55 > 54 → son = 5.

0517210317423534655767899↑ ortabasson

Adım 3: orta = (5 + 5) / 2 = 5 → 34 < 54 → bas = 6.

0517210317423534655767899↑ bas↑ son

bas = 6 > son = 5: aralık boş → 3 adımda bulunamadı, −1 döner. 54 olsaydı 34 ile 55 arasında (indis 6'da) olurdu.

İkili Arama · Kodu adım adım çalıştır

ikiliArama(dizi, 9, 47): Kodu Adım Adım İzle

001427310414523645747853↑ bas↑ son

Aranan 47; bas = 0, son = 8.

001427310414523645747853↑ orta↑ bas↑ son

bas ≤ son → orta = (0 + 8) / 2 = 4; dizi[4] = 14.

001427310414523645747853↑ bas↑ son

47 > 14 → bas = 5; sol kısım elenir.

001427310414523645747853↑ orta↑ bas↑ son

bas ≤ son → orta = (5 + 8) / 2 = 6; dizi[6] = 45.

001427310414523645747853↑ bas↑ son

47 > 45 → bas = 7; sol kısım elenir.

001427310414523645747853↑ ortabas↑ son

bas ≤ son → orta = (7 + 8) / 2 = 7; dizi[7] = 47.

001427310414523645747853↑ ortabas↑ son

47 == dizi[7] → 7 döner. 9 elemanda 3 karşılaştırma (en fazla ⌊log₂ 9⌋ + 1 = 4).

int ikiliArama(int[] dizi, int n, int aranan) {
  int bas = 0, son = n - 1;
  while (bas <= son) {
    int orta = (bas + son) / 2;
    if (aranan < dizi[orta])
      son = orta - 1;      // sol yarı
    else if (aranan > dizi[orta])
      bas = orta + 1;      // sağ yarı
    else
      return orta;         // bulundu
  }
  return -1;
}

İkili Arama

İkili Arama: Karmaşıklık

T(n) = T(n/2) + 1 ⇒ T(n) = O(log n)

  • Her turda aralık en az yarıya iner: n → n/2 → n/4 → … → 1.
  • k tur sonra n/2ᵏ ≤ 1 olur: k ≈ log₂ n.
  • Ek bellek: yinelemeli sürümde O(1), özyinelemeli sürümde O(log n) yığın.
DurumMertebe
En iyi (ilk orta eşleşir)O(1)
OrtalamaO(log n)
En kötü / başarısızO(log n)
Ön hazırlık (sıralama)O(n log n), bir kez
Ne zaman değer? Dizi bir kez sıralanıp çok kez aranıyorsa: k sorgu için doğrusal O(k · n), sırala + ikili O(n log n + k log n). k büyüdükçe fark açılır.

İkili Arama

İkili Arama: Uygulama Ayrıntıları

Taşma (overflow)(bas + son) / 2, bas + son > 2³¹ − 1 olduğunda negatif olur. Güvenli yazım: bas + (son - bas) / 2 ya da (bas + son) >>> 1.
Sınırların güncellenmesison = orta ya da bas = orta yazmak aralığı küçültmeyebilir → sonsuz döngü. Orta eleman zaten denetlendiği için ±1 şarttır.
Tekrarlı elemanlarEşitlikte hemen dönmek herhangi bir eşleşmeyi verir. İlk konum için eşitlikte sonucu kaydedip son = orta − 1 ile sola devam edilir.
Java kütüphanesiArrays.binarySearch(dizi, x): bulunamazsa −(ekleme noktası) − 1 döndürür; böylece x'in sırayı bozmadan nereye ekleneceği de öğrenilir.

Hash ile Arama

Hash Tablo Arama Algoritması

  • Bir anahtarın kilide tam oturmasına benzer mantıkla çalışır.
  • Veriler, anahtarlar ve onlara karşılık gelen değerlerden oluşur.
  • Anahtar, hash fonksiyonu ile bir tablo indisine (hash değeri) dönüştürülür; bu değer verinin tablodaki yerini işaret eder.
  • Arama, anahtarın hash değerine karşılık gelen konumda (kova, bucket) yapılır.
  • Kova boş değilse anahtar, kovadaki değerlerle karşılaştırılır; eşleşme bulunursa işlem tamamlanır.
75anahtar75 % 10hash fonksiyonu2345675
Diğer aramalar elemanları karşılaştırarak yer arar; hash araması yeri hesaplar. Bu yüzden ortalama süre n'den bağımsızdır: O(1).

Hash ile Arama

Hash ile Arama: Avantajlar ve Dezavantajlar

Avantajlar

  • Ortalama durumda çok hızlı arama: O(1).
  • Verilerin önceden sıralanmasına gerek yoktur.
  • Büyük veri kümelerinde aramalar için idealdir.
  • Hash fonksiyonu iyi seçilmişse aranan öğeye doğrudan erişilir.

Dezavantajlar

  • Hash fonksiyonu çakışmalara (collision) yol açabilir: iki farklı anahtarın aynı hash değerine sahip olması.
  • Çakışma olduğunda ek işlem adımları gerekir; en kötü durumda O(n).
  • Ek bellek (boş kovalar) ister; sıralı gezinme, en küçük / aralık sorguları desteklenmez.

Hash ile Arama

Hash Fonksiyonu

int hashFonksiyonu(String anahtar,
                   int m) {
  int toplam = 0;
  for (char k : anahtar.toCharArray())
    toplam += k;        // Unicode kodu
  return toplam % m;    // m: tablo boyu
}
anahtarkarakter kodlarıtoplam % 10
"ali"97 + 108 + 105 = 3100
"ila"105 + 108 + 97 = 3100
"veli"118 + 101 + 108 + 105 = 4322
  • Bu fonksiyon, karakterlerin Unicode kodlarını (Java'da char'ın sayısal değeri) toplar ve tablo boyuna göre mod alır.
  • Basit ama zayıftır: harfleri aynı olan kelimeler ("ali" ve "ila") her zaman çakışır.
  • İyi bir hash fonksiyonu hızlı hesaplanır ve anahtarları kovalara düzgün dağıtır; Java'nın String.hashCode()'u konuma bağlı ağırlık kullanır: Σ s[i] · 31ⁿ⁻¹⁻ⁱ.

Hash ile Arama

Hash Tablo Arama Algoritması (Doğrusal Yoklama)

int hashArama(Integer[] tablo, int anahtar) {
  int m = tablo.length;
  int indis = anahtar % m;   // hash
  while (tablo[indis] != null) {
    if (tablo[indis] == anahtar)
      return indis;          // bulundu
    indis = (indis + 1) % m; // sonraki kova
  }
  return -1;                 // boş kova: yok
}
  • Veri yapıları: tablo anahtarları saklar (genel uygulamada anahtar-değer çiftleri); hash fonksiyonu anahtarı indise dönüştürür.
  • while, tablo[indis] boş (null) olana kadar sürer.
  • Her turda kovadaki anahtar aranan ile karşılaştırılır; eşleşirse konum (anahtar-değer tablosunda değer) döner.
  • Eşleşme yoksa bir sonraki kovaya bakılır; % m ile sondan başa dönülür (döngüsel arama).
  • Boş kovaya varılırsa anahtar yoktur.
Bu çakışma çözümüne açık adresleme (doğrusal yoklama, linear probing) denir. Örneklerde anahtarlar tamsayıdır ve h(x) = x % 10 kullanılır.

Hash ile Arama · Adım adım

Doğrusal Yoklama ile Ekleme

0—1—2—3—4—5—6—7—8—9—

Boş tablo (m = 10). Anahtarlar sırayla eklenecek: 16, 12, 45, 29, 6, 122, 5, 58, 75; h(x) = x % 10.

0—1—2—3—4—5—6167—8—9—↑ h(16)

h(16) = 6: kova boş → 16 buraya yerleşir.

0—1—2123—4—5—6167—8—9—↑ h(12)

h(12) = 2: kova boş → 12 buraya yerleşir.

0—1—2123—4—5456167—8—9—↑ h(45)

h(45) = 5: kova boş → 45 buraya yerleşir.

0—1—2123—4—5456167—8—929↑ h(29)

h(29) = 9: kova boş → 29 buraya yerleşir.

0—1—2123—4—545616768—929↑ h(6)

h(6) = 6 dolu → 7 yoklanır; ilk boş kova 7. (2 yoklama)

0—1—21231224—545616768—929↑ h(122)

h(122) = 2 dolu → 3 yoklanır; ilk boş kova 3. (2 yoklama)

0—1—21231224—5456167685929↑ h(5)

h(5) = 5 dolu → 6, 7, 8 yoklanır; ilk boş kova 8. (4 yoklama)

0581—21231224—5456167685929↑ h(58)

h(58) = 8 dolu → 9, 0 yoklanır; ilk boş kova 0. (3 yoklama)

05817521231224—5456167685929↑ h(75)

h(75) = 5 dolu → 6, 7, 8, 9, 0, 1 yoklanır; ilk boş kova 1. (7 yoklama) Dolu kovalar bitişik bir küme (5, 6, …, 9, 0, …, 3; yalnızca kova 4 boş) oluşturdu: birincil kümelenme.

Hash ile Arama · Kodu adım adım çalıştır

hashArama(tablo, 75): Kodu Adım Adım İzle

05817521231224—5456167685929↑ indis

m = 10, indis = 75 % 10 = 5.

05817521231224—5456167685929↑ indis

tablo[5] = 45 ≠ 75 → indis = (5 + 1) % 10 = 6.

05817521231224—5456167685929↑ indis

tablo[6] = 16 ≠ 75 → indis = (6 + 1) % 10 = 7.

05817521231224—5456167685929↑ indis

tablo[7] = 6 ≠ 75 → indis = (7 + 1) % 10 = 8.

05817521231224—5456167685929↑ indis

tablo[8] = 5 ≠ 75 → indis = (8 + 1) % 10 = 9.

05817521231224—5456167685929↑ indis

tablo[9] = 29 ≠ 75 → indis = (9 + 1) % 10 = 0. Sondan başa dönüldü.

05817521231224—5456167685929↑ indis

tablo[0] = 58 ≠ 75 → indis = (0 + 1) % 10 = 1.

05817521231224—5456167685929↑ indis

tablo[1] = 75 → 1 döner. 7 yoklama: 75'in yerine kümenin sonunda ulaşıldı.

int hashArama(Integer[] tablo, int anahtar) {
  int m = tablo.length;
  int indis = anahtar % m;   // hash
  while (tablo[indis] != null) {
    if (tablo[indis] == anahtar)
      return indis;          // bulundu
    indis = (indis + 1) % m; // sonraki kova
  }
  return -1;                 // boş kova: yok
}

Hash ile Arama · Adım adım

Zincirleme (Chaining) ile Ekleme ve Arama

anahtarlar:16124529612255875h(x) = x % 100123456789

Ayrı zincirleme (separate chaining): her kova bir bağlı listenin başını tutar. Aynı anahtarlar, h(x) = x % 10.

anahtarlar:16124529612255875h(16) = 6012345616789

h(16) = 6 → kova 6 boş; 16 listenin sonuna eklenir.

anahtarlar:16124529612255875h(12) = 201212345616789

h(12) = 2 → kova 2 boş; 12 listenin sonuna eklenir.

anahtarlar:16124529612255875h(45) = 50121234545616789

h(45) = 5 → kova 5 boş; 45 listenin sonuna eklenir.

anahtarlar:16124529612255875h(29) = 9012123454561678929

h(29) = 9 → kova 9 boş; 29 listenin sonuna eklenir.

anahtarlar:16124529612255875h(6) = 60121234545616678929

h(6) = 6 → kova 6 dolu (çakışma); 6 listenin sonuna eklenir.

anahtarlar:16124529612255875h(122) = 20121212234545616678929

h(122) = 2 → kova 2 dolu (çakışma); 122 listenin sonuna eklenir.

anahtarlar:16124529612255875h(5) = 501212122345455616678929

h(5) = 5 → kova 5 dolu (çakışma); 5 listenin sonuna eklenir.

anahtarlar:16124529612255875h(58) = 80121212234545561667858929

h(58) = 8 → kova 8 boş; 58 listenin sonuna eklenir.

anahtarlar:16124529612255875h(75) = 5012121223454557561667858929

h(75) = 5 → kova 5 dolu (çakışma); 75 listenin sonuna eklenir.

anahtarlar:16124529612255875ara(75): kova 5012121223454557561667858929

Arama 75: kova 5'in listesi gezilir: 45 ≠ 75, 5 ≠ 75, 75 = 75 → bulundu (3 karşılaştırma). Doğrusal yoklamadaki 7 yoklamaya göre daha az.

Hash ile Arama

Yük Faktörü ve Karmaşıklık

yük faktörü α = n / m  (n: anahtar sayısı, m: kova sayısı)

YöntemBaşarılı arama (ort.)Başarısız arama (ort.)α = 0,5α = 0,9
Zincirleme≈ 1 + α/2≈ 1 + α1,25 / 1,51,45 / 1,9
Doğrusal yoklama≈ ½ (1 + 1/(1 − α))≈ ½ (1 + 1/(1 − α)²)1,5 / 2,55,5 / 50,5
  • α sabit tutulursa (ör. α ≤ 0,75 olunca tabloyu iki katına çıkarıp yeniden yerleştirmek) ortalama arama O(1) olur.
  • En kötü durum O(n): tüm anahtarlar aynı kovaya düşerse (kötü hash fonksiyonu) arama doğrusal aramaya dönüşür.
  • Doğrusal yoklamada α → 1 iken kümelenme maliyeti hızla büyütür; tablo tamamen dolarsa başarısız aramada döngü hiç bitmez.

Formüller Knuth'un düzgün hash varsayımı altındaki yaklaşık değerleridir.

Ara Değer Arama

Ara Değer (Interpolation) Arama

  • Sıralı veri kümelerinde kullanılır.
  • Verilerin listede eşit aralıklarla dağıldığını varsayar: başlangıç değerinden sonra ardışık elemanlar arasındaki fark yaklaşık aynıdır.
  • Bu varsayımla aranan değerin konumu, en küçük ve en büyük eleman kullanılarak tahmin edilir.
  • Tahmin edilen konum kontrol edilir; yanlışsa arama daha dar bir aralıkta (sağda veya solda) sürer.
  • Telefon rehberinde "Yılmaz"ı ortadan değil, sona yakın bir yerden açmaya benzer.

indis = bas + (aranan − dizi[bas]) · (son − bas) / (dizi[son] − dizi[bas])

Örnek: 10, 20, …, 100 dizisinde 70: indis = 0 + (70 − 10) · 9 / 90 = 6 → dizi[6] = 70. Düzgün dağılımda tek yoklamada bulunur.

İkili arama daima ortaya bakar (oran ½); ara değer arama oranı değerlerden hesaplar.

Ara Değer Arama

Ara Değer Arama Algoritması

int araDegerArama(int[] dizi, int n,
                  int aranan) {
  int bas = 0, son = n - 1;
  while (bas <= son && aranan >= dizi[bas]
                    && aranan <= dizi[son]) {
    if (dizi[son] == dizi[bas]) return bas; // 0'a bölme
    int pay = (aranan - dizi[bas]) * (son - bas);
    int indis = bas + pay / (dizi[son] - dizi[bas]);
    if (dizi[indis] == aranan) return indis;
    if (dizi[indis] < aranan) bas = indis + 1;
    else son = indis - 1;
  }
  return -1;
}
  • bas ve son arama aralığının sınırlarıdır; dizi[son] == dizi[bas] ise aranan bu değere eşittir (0'a bölme önlenir).
  • Aranan değer aralığın uç değerlerinden küçük veya büyükse döngüye girilmez, −1 döner.
  • Tahmini indis hesaplanır ve kontrol edilir:
    • eşitse indis döner;
    • dizi[indis] küçükse indis + 1'den,
    • büyükse indis − 1'e kadar devam edilir.
  • Aralık boşalırsa −1 döner.

Ara Değer Arama · Kodu adım adım çalıştır

araDegerArama(dizi, 15, 24): Kodu Adım Adım İzle

10012113216318419520621722823924103311351242134714↑ bas↑ son

Aranan 24. Çubuk yüksekliği değeri gösterir: dağılım yaklaşık düzgündür.

10012113216318419520621722823924103311351242134714↑ indis↑ bas↑ son

indis = 0 + (24 − 10)·14 / (47 − 10) = 0 + 196/37 → 5; dizi[5] = 19.

10012113216318419520621722823924103311351242134714↑ bas↑ son

19 < 24 → bas = 6.

10012113216318419520621722823924103311351242134714↑ indis↑ bas↑ son

indis = 6 + (24 − 20)·8 / (47 − 20) = 6 + 32/27 → 7; dizi[7] = 21.

10012113216318419520621722823924103311351242134714↑ bas↑ son

21 < 24 → bas = 8.

10012113216318419520621722823924103311351242134714↑ indisbas↑ son

indis = 8 + (24 − 22)·6 / (47 − 22) = 8 + 12/25 → 8; dizi[8] = 22.

10012113216318419520621722823924103311351242134714↑ bas↑ son

22 < 24 → bas = 9.

10012113216318419520621722823924103311351242134714↑ indisbas↑ son

indis = 9 + (24 − 23)·5 / (47 − 23) = 9 + 5/24 → 9; dizi[9] = 23.

10012113216318419520621722823924103311351242134714↑ bas↑ son

23 < 24 → bas = 10.

10012113216318419520621722823924103311351242134714↑ indisbas↑ son

indis = 10 + (24 − 24)·4 / (47 − 24) = 10 + 0/23 → 10; dizi[10] = 24. Eşit → 10 döner (5 yoklama).

int araDegerArama(int[] dizi, int n,
                  int aranan) {
  int bas = 0, son = n - 1;
  while (bas <= son && aranan >= dizi[bas]
                    && aranan <= dizi[son]) {
    if (dizi[son] == dizi[bas]) return bas; // 0'a bölme
    int pay = (aranan - dizi[bas]) * (son - bas);
    int indis = bas + pay / (dizi[son] - dizi[bas]);
    if (dizi[indis] == aranan) return indis;
    if (dizi[indis] < aranan) bas = indis + 1;
    else son = indis - 1;
  }
  return -1;
}

Ara Değer Arama

Ara Değer Arama: Karmaşıklık ve Kötü Durum

DurumMertebe
En iyiO(1)
Ortalama (düzgün dağılım)O(log log n)
En kötü (çarpık dağılım)O(n)
Ek bellekO(1)
  • n = 10⁶ için log₂ log₂ n ≈ 4,3; ikili arama ≈ 20 yoklama.
  • Her yoklama ikili aramadan pahalıdır (çarpma, bölme).
  • Ara çarpım (aranan − dizi[bas]) · (son − bas) büyük değerlerde int'i taşırabilir; long kullanın.
Kötü durum örneği: dizi = 1, 2, …, 9, 1000 ve aranan 9. Uç değer 1000 tahmini hep sola çeker: indisler 0, 1, 2, 3, 4, 5, 6, 7, 8 → 9 yoklama. İkili arama aynı aramayı 4, 7, 8 indisleriyle 3 yoklamada bitirir.

Atlayarak Arama

Atlayarak Ara Bul (Jump Search)

  • Engelli koşuda engelleri aşarak ilerleyen atlet mantığıyla çalışır; dizi sıralı olmalıdır.
  • Liste belirlenen büyüklükte (m = √n) parçalara (bloklara) ayrılır.
  • Listenin başından büyük bir adım atlanır ve o konumdaki öğe (bloğun son elemanı) kontrol edilir.
  • Aranan değer bu konumdaysa arama biter; daha büyükse atlamaya devam edilir.
  • Değer atlanan parçadaysa, o aralığa geri dönülerek doğrusal arama yapılır.
123456789atlaatlablok içinde doğrusal arama
Geri dönüş, ikili aramadaki gibi büyük sıçramalar gerektirmez: veri sırayla okunur. Bu yüzden geriye gitmenin pahalı olduğu ortamlarda (manyetik bant, blok blok okunan disk) tercih edilir.

Atlayarak Arama

Atlayarak Ara Bul Algoritması

int atlayarakAraBul(int[] dizi, int eleman,
                    int n) {
  int blok = (int) Math.sqrt(n);
  int atlama = blok, konum = 0;
  while (dizi[Math.min(atlama, n) - 1] < eleman) {
    konum = atlama; atlama += blok; // atla
    if (konum >= n) return -1;
  }
  while (dizi[konum] < eleman) {     // doğrusal
    konum++;
    if (konum == Math.min(atlama, n)) return -1;
  }
  if (dizi[konum] == eleman) return konum;
  return -1;
}
  • Atlama mesafesi Math.sqrt(n) ile hesaplanır; konum başta 0'dır.
  • İlk döngüde her bloğun son elemanına bakılır; aranandan küçük oldukça bir blok atlanır.
  • Döngü sonunda aranan, [konum, atlama) aralığında olabilir.
  • İkinci döngü bu aralığı doğrusal tarar.
  • Bulunursa indis, yoksa −1 döner.

Atlayarak Arama · Kodu adım adım çalıştır

atlayarakAraBul(dizi, 69, 10): Kodu Adım Adım İzle

0219230332438547661769879981↑ konum↑ atlama−1

Aranan 69; n = 10 → blok = ⌊√10⌋ = 3; atlama = 3, konum = 0.

0219230332438547661769879981↑ 30 < 69

dizi[min(3, 10) − 1] = dizi[2] = 30 < 69 → bir blok atla: konum = 3, atlama = 6.

0219230332438547661769879981↑ 47 < 69

dizi[min(6, 10) − 1] = dizi[5] = 47 < 69 → bir blok atla: konum = 6, atlama = 9.

0219230332438547661769879981↑ 79 ≥ 69↑ konum

dizi[8] = 79 ≥ 69 → atlama biter. Aranan, [6, 9) bloğunda olabilir.

0219230332438547661769879981↑ konum

Doğrusal arama: dizi[6] = 61 < 69 → konum = 7.

0219230332438547661769879981↑ konum

dizi[7] = 69 → 7 döner. Yoklananlar: 2, 5, 8, 6, 7.

int atlayarakAraBul(int[] dizi, int eleman,
                    int n) {
  int blok = (int) Math.sqrt(n);
  int atlama = blok, konum = 0;
  while (dizi[Math.min(atlama, n) - 1] < eleman) {
    konum = atlama; atlama += blok; // atla
    if (konum >= n) return -1;
  }
  while (dizi[konum] < eleman) {     // doğrusal
    konum++;
    if (konum == Math.min(atlama, n)) return -1;
  }
  if (dizi[konum] == eleman) return konum;
  return -1;
}

Atlayarak Arama

Atlayarak Arama: Neden √n?

en kötü yoklama ≈ n/m + (m − 1)

  • n/m: en fazla atlama sayısı; m − 1: blok içindeki doğrusal arama.
  • Türev: −n/m² + 1 = 0 ⇒ m = √n; toplam ≈ 2√n − 1.
  • Karmaşıklık O(√n): doğrusal O(n) ile ikili O(log n) arasında.
  • Ek bellek O(1); dizi sıralı olmalıdır.
nm = √n≈ 2√n − 1ikili
10010197
10 00010019914
10⁶1 0001 99920

Blok bulunduktan sonra blok sondan geriye (8 → 7) ya da atlayarakAraBul kodundaki gibi baştan ileri (6 → 7) taranabilir. İkisi de en fazla m − 1 ek yoklama yapar.

İyileştirme: blok bulunduktan sonra içeride doğrusal yerine yine atlayarak (√m adımla) ya da ikili arama yapılabilir. İkili arama varken atlayarak arama, yalnızca geri gitmenin pahalı olduğu durumlarda anlamlıdır.

Üstel Arama

Üstel (Exponential) Arama

  • Sıralı listeyi üstel olarak büyüyen parçalara ayırır: sınır (konum) 1, 2, 4, 8, 16, … diye ikiye katlanır.
  • dizi[konum] aranandan büyük olunca durulur: aranan [konum/2, konum] aralığındadır.
  • Bu aralıkta ikili arama yapılır (Bentley ve Yao, 1976).
  • Örneğin 1024 elemanlı bir listede aranan indis 600 civarındaysa sınırlar 1, 2, …, 512, 1024'tür; arama 512 ile 1023 arasında ikili aramayla sürer.
0123456789101112131415ikili arama [4, 8]1×2×2×2
Neden? Dizinin boyu bilinmiyorsa (sınırsız / akan veri) ya da aranan başa yakınsa idealdir: maliyet n'ye değil, aranan elemanın konumuna (i) bağlıdır.

Üstel Arama

Üstel Arama Algoritması

int ustelArama(int[] dizi, int aranan, int n) {
  if (dizi[0] == aranan) return 0;
  int konum = 1;
  while (konum < n && dizi[konum] <= aranan)
    konum *= 2;               // 1, 2, 4, 8, ...
  int bas = konum / 2, son = Math.min(konum, n - 1);
  while (bas <= son) {        // ikili arama
    int orta = (bas + son) / 2;
    if (dizi[orta] == aranan) return orta;
    if (dizi[orta] < aranan) bas = orta + 1;
    else son = orta - 1;
  }
  return -1;
}
  • konum başta 1'dir; dizi[konum] ≤ aranan oldukça 2 ile çarpılır.
  • Döngü sonunda konum, aranılan değerin olabileceği aralığın sonunu işaret eder; konum / 2 başıdır.
  • Math.min(konum, n − 1): sınır dizinin dışına taşmamalıdır.
  • Kalan kısım ikili aramayla taranır.
Son aşamada ikili arama yerine doğrusal tarama (konum / 2'den sona doğru) yapılırsa maliyet O(i) olur ve aranan yoksa dizinin sonuna kadar gidilir. İkili arama ile maliyet O(log i)'ye iner.

Üstel Arama · Kodu adım adım çalıştır

ustelArama(dizi, 47, 10): Kodu Adım Adım İzle

0219230332438547661769879981↑ 2 ≠ 47

Aranan 47. Önce dizi[0] = 2 ≠ 47.

0219230332438547661769879981↑ konum

dizi[1] = 9 ≤ 47 → konum = 2.

0219230332438547661769879981↑ konum

dizi[2] = 30 ≤ 47 → konum = 4.

0219230332438547661769879981↑ konum

dizi[4] = 38 ≤ 47 → konum = 8.

0219230332438547661769879981↑ konum

dizi[8] = 79 > 47 → katlama biter. Aranan [8/2, min(8, 9)] = [4, 8] aralığındadır.

0219230332438547661769879981↑ orta↑ bas↑ son

İkili arama: orta = (4 + 8) / 2 = 6 → dizi[6] = 61 > 47 → son = 5.

0219230332438547661769879981↑ ortabas↑ son

İkili arama: orta = (4 + 5) / 2 = 4 → dizi[4] = 38 < 47 → bas = 5.

0219230332438547661769879981↑ ortabasson

İkili arama: orta = (5 + 5) / 2 = 5 → dizi[5] = 47 = 47 → 5 döner.

int ustelArama(int[] dizi, int aranan, int n) {
  if (dizi[0] == aranan) return 0;
  int konum = 1;
  while (konum < n && dizi[konum] <= aranan)
    konum *= 2;               // 1, 2, 4, 8, ...
  int bas = konum / 2, son = Math.min(konum, n - 1);
  while (bas <= son) {        // ikili arama
    int orta = (bas + son) / 2;
    if (dizi[orta] == aranan) return orta;
    if (dizi[orta] < aranan) bas = orta + 1;
    else son = orta - 1;
  }
  return -1;
}

Üstel Arama

Üstel Arama: Karmaşıklık

  • Aranan i indisindeyse katlama aşaması ⌊log₂ i⌋ + 1 adım sürer.
  • Son aralık [i/2, i] en fazla i/2 + 1 elemanlıdır → ikili arama ≈ log₂ i adım.
  • Toplam: O(log i); en kötü (i ≈ n) O(log n), en iyi O(1). Ek bellek O(1).
  • Aranan başa yakınsa ikili aramadan hızlıdır; sona yakınsa ≈ 2 kat yavaştır.
i (n = 10⁶)üstel (≈ 2 log₂ i)ikili (≈ log₂ n)
10≈ 720
1 000≈ 2020
500 000≈ 3820
Kullanım: sınırsız (boyu bilinmeyen) sıralı listeler, büyük bir dosyada başa yakın kayıtlar ve sıralı veri akışlarında "galop" (galloping) araması; örneğin Timsort birleştirmede bu fikri kullanır.

Karşılaştırma

Arama Algoritmalarının Karşılaştırılması

AlgoritmaÖnkoşulEn iyiOrtalamaEn kötüEk bellek
DoğrusalyokO(1)O(n)O(n)O(1)
İkilisıralıO(1)O(log n)O(log n)O(1)
Hash (tablo)hash tablosuO(1)O(1)O(n)O(m)
Ara değersıralı, düzgünO(1)O(log log n)O(n)O(1)
AtlayaraksıralıO(1)O(√n)O(√n)O(1)
ÜstelsıralıO(1)O(log i)O(log n)O(1)

i: aranan elemanın indisi, m: kova sayısı. Hash tablosunun ortalama O(1)'i, yük faktörünün sabit tutulmasına bağlıdır. Sıralama gerekiyorsa ona bir kez O(n log n) eklenir.

Karşılaştırma

Aynı Dizide Yoklama Sayıları

Dizi: 2, 9, 30, 32, 38, 47, 61, 69, 79, 81 (atlayarak ve üstel arama örneklerindeki dizi). Hücre: yoklama sayısı (yoklanan indisler).

arananDoğrusalİkiliAtlayarakAra değerÜstelsonuç
698 (0,1,2,3,4,5,6,7)2 (4,7)5 (2,5,8,6,7)1 (7)7 (0,1,2,4,8,6,7)7
476 (0,1,2,3,4,5)3 (4,7,5)5 (2,5,3,4,5)1 (5)8 (0,1,2,4,8,6,4,5)5
21 (0)3 (4,1,0)2 (2,0)1 (0)1 (0)0
5010 (0,1,2,3,4,5,6,7,8,9)4 (4,7,5,6)4 (2,5,8,6)1 (5)8 (0,1,2,4,8,6,4,5)-1
Dizi neredeyse düzgün dağıldığı için ara değer arama her sorguda tek yoklama yapar. 50 aranırken ara değer arama 47'yi yoklar, ardından aranan aralık dışında kaldığı için hemen −1 döner. Üstel aramada 4 indisi iki kez (katlama ve ikili arama aşamasında) yoklanır.

Karşılaştırma

Hangi Algoritma Ne Zaman?

DoğrusalSırasız ya da küçük veri; bağlı liste; tek seferlik arama.
İkiliSıralı dizi, çok sayıda sorgu; genel amaçlı varsayılan seçim.
HashYalnızca tam eşleşme sorgusu ve çok sayıda ekleme / arama; sıra gerekmiyor.
Ara değerSıralı ve düzgün dağılmış sayısal anahtarlar (ör. sıralı kimlik numaraları).
AtlayarakGeriye gitmenin pahalı olduğu sıralı ortamlar (bant, blok okuma).
ÜstelBoyu bilinmeyen sıralı liste ya da aranan başa yakın.
Soru sırası: Veri sıralı mı? Hayır → doğrusal ya da hash tablosu kur. Evet → ikili arama; dağılım düzgünse ara değer, boy bilinmiyorsa üstel, geri gitmek pahalıysa atlayarak.

Karşılaştırma

Deneyin: Aynı Dizide Beş Algoritma

Hücrenin üstündeki sayı yoklama sırasıdır; yeşil: bulundu. Tablo, son aranan değer için beş algoritmanın yoklama sayılarını karşılaştırır. Çarpık dizide ara değer aramanın nasıl yavaşladığını görün.

Özet

Özet

KonuHatırlanacaklar
Doğrusalbaştan sona tek tek karşılaştır; önkoşul yok; ortalama (n + 1)/2, en kötü n → O(n)
İkilisıralı dizi; orta ile karşılaştır, yarıyı ele; en fazla ⌊log₂ n⌋ + 1 → O(log n)
Hashyeri hesapla: h(anahtar); çakışma → doğrusal yoklama ya da zincirleme; ort. O(1), en kötü O(n)
Ara değerkonumu değer oranıyla tahmin et; düzgün dağılımda O(log log n), çarpıkta O(n)
Atlayarak√n'lik bloklar atla, sonra blok içinde doğrusal ara → O(√n)
Üstelsınırı 1, 2, 4, … katla, [konum/2, konum]'da ikili arama → O(log i)

Özet

Etkileşimli Simülatörler

Arama AlgoritmalarıRastgele 16 elemanlı bir dizide doğrusal, ikili ve atlayarak aramayı adım adım canlandırır; ikili ve atlayarak aramadan önce dizi sıralanır, yoklanan hücre vurgulanır.
Hash ve Ara Değer Arama8 kovalı (x mod 8) bir hash tablosunda kova aramasını ve 12 elemanlı sıralı dizide ara değer aramasını yan yana dener; yoklama sayısını raporlar.
Üstel Arama20 elemanlı sıralı dizide sınırı 1, 2, 4, 8, … diye büyütür, ardından bulunan aralıkta ikili arama yapar ve tüm yoklama dizisini yazar.

Özet

Sık Yapılan Hatalar

Sırasız dizide ikili aramaHata vermez, ama yanlış sonuç (−1) döndürebilir. İkili, ara değer, atlayarak ve üstel arama sıralı dizi ister.
Sınırlarda ±1 hatasıwhile (bas < son) tek elemanlı aralığı atlar; son = orta sonsuz döngüye sokabilir.
Orta hesabında taşmaÇok büyük dizilerde (bas + son) / 2 taşar; bas + (son − bas) / 2 yazın.
Ara değerde bölmedizi[son] == dizi[bas] ise sıfıra bölme; aranan uç değerlerin dışındaysa indis dizi dışına çıkar. Önce aralığı denetleyin.
Dolu hash tablosuDoğrusal yoklamada boş kova kalmazsa arama döngüsü bitmez. Yük faktörünü sınırlayın ya da m kez yoklayınca durun.
Üstel aramada sınırMath.min(konum, n − 1) unutulursa ArrayIndexOutOfBoundsException; son aşamada doğrusal tarama O(log i)'yi bozar.

Özet

Örnek Problemler

Dizi D = 3, 8, 15, 21, 27, 34, 40, 46, 52, 59, 65, 71, 78, 84, 90, 97 (n = 16, indisler 0'dan).

  1. İkili aramada 65 için hangi indisler yoklanır?
  2. Atlayarak aramada 65 için yoklanan indisler nelerdir?
  3. Ara değer arama 65 ve 30'u kaç yoklamada sonuçlandırır?
  4. Üstel aramada 65 için yoklanan indisler nelerdir?
  5. m = 7, h(x) = x % 7, doğrusal yoklama: 10, 24, 17, 31, 5 eklenir. Son tablo? 12 aranırken kaç kova yoklanır?
Cevapları göster
  1. orta: 7, 11, 9, 10 → indis 10 (4 yoklama).
  2. blok = 4: blok sonları 3, 7, 11, sonra doğrusal 8, 9, 10 → indis 10 (6 yoklama).
  3. 65: indis 0 + (65 − 3)·15/94 = 9 (59 < 65), sonra 10 + 0 = 10 → 2 yoklama. 30: indis 4 (27 < 30) → bas = 5, 30 < dizi[5] = 34 olduğundan döngü biter → 1 yoklama, −1.
  4. 0; 1, 2, 4, 8 (52 ≤ 65), 16 ≥ n → aralık [8, 15]; orta 11, 9, 10 → indis 10 (8 yoklama).
  5. [5, —, —, 10, 24, 17, 31] (31 → 3, 4, 5, 6; 5 → 5, 6, 0). 12: h = 5 → 5, 6, 0, 1 (boş) → 4 yoklama, bulunamadı.

Özet

Alıştırmalar

  1. İkili aramanın özyinelemeli sürümünü yazın; ek bellek karmaşıklığı nedir?
  2. Tekrarlı elemanlar içeren sıralı dizide aranan değerin ilk konumunu bulan ikili arama yazın.
  3. Başarılı doğrusal aramada ortalama karşılaştırma sayısının (n + 1)/2 olduğunu gösterin.
  4. Atlayarak aramada en iyi blok boyunun √n olduğunu gösterin; n = 10 000 için en kötü yoklama sayısı?
  5. 10, 20, …, 100 dizisinde ara değer arama 70'i kaç yoklamada bulur? 1, 2, …, 9, 1000 dizisinde 9'u?
  6. Zincirleme hash tablosunda (m = 10) 16, 12, 45, 29, 6, 122, 5, 58, 75 varken 35 aranırsa kaç anahtar karşılaştırılır?
Cevaplar / ipuçları
  1. ara(dizi, x, bas, son): bas > son ise −1; orta ile karşılaştırıp yarıda kendini çağırır. Yığın derinliği O(log n).
  2. Eşitlikte sonuc = orta; son = orta − 1; ile sola devam edin; döngü sonunda sonuc döner. Hâlâ O(log n).
  3. i. eleman i karşılaştırmayla bulunur, olasılık 1/n: Σ i/n = n(n + 1)/(2n) = (n + 1)/2.
  4. f(m) = n/m + m − 1, f′(m) = −n/m² + 1 = 0 ⇒ m = √n. n = 10 000: 100 + 99 = 199.
  5. 70: indis = 0 + 60·9/90 = 6 → 1 yoklama. 9: indisler 0, 1, …, 8 → 9 yoklama (çarpık dağılım).
  6. h(35) = 5 → kova 5 listesi 45, 5, 75: 3 karşılaştırma, bulunamadı.

Özet

Kaynaklar

  • Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, MIT Press, 2022 (Bölüm 2.3, 11).
  • Sedgewick ve Wayne, Algorithms, 4. baskı, 2011 (Bölüm 3.1 İkili arama, 3.4 Hash tabloları).
  • D. E. Knuth, The Art of Computer Programming, Vol. 3: Sorting and Searching, 2. baskı, 1998.
  • J. L. Bentley ve A. C. Yao, "An Almost Optimal Algorithm for Unbounded Searching", Information Processing Letters 5(3), 1976.
  • Y. Perl, A. Itai ve H. Avni, "Interpolation Search — A Log Log N Search", Communications of the ACM 21(7), 1978.
  • B. Shneiderman, "Jump Searching: A Fast Sequential Search Technique", Communications of the ACM 21(10), 1978.

Son

Bölüm 3: Arama Algoritmaları
Sonraki bölüm: Bölüm 4.1: Çizge Gezinme Algoritmaları
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.