Bölüm 3: Arama Algoritmaları
Giriş
Öğrenme Çıktıları
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 fonksiyonu, kova, çakışma, doğrusal yoklama ve zincirleme kavramlarını açıklamak; yük faktörünün maliyete etkisini yorumlamak.
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.
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.
dizi ve bir aranan değer.dizi[i] == aranan olan bir indis i; aranan yoksa −1 (başarısız arama).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) | yok | Verileri tek tek karşılaştırarak arar. |
| İkili (Binary Search) | sıralı dizi | Veri kümesini ikiye bölerek arama alanını daraltır. |
| Hash (Hash Search) | hash tablosu | Veriler 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ı dizi | Sı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.
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:
diziaranacak öğeler,ndizinin boyutu,arananaranacak öğe. - Dönüş: aranan öğenin indisi; bulunamazsa −1.
idizideki öğeleri dolaşır;whiledö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.
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
En iyi durum: 33 aranır. Dizi sırasızdır: 33, 21, 12, 7, 8, 1, 3, 44, 56, 18 (n = 10).
dizi[0] = 33 = aranan → döngü hemen biter; 0 döner. Yalnızca 1 karşılaştırma: en iyi durum O(1).
En kötü durum: 17 aranır. 17 dizide yoktur; her elemana bakmak gerekir.
i = 0: dizi[0] = 33 ≠ 17 → i = 1.
i = 1: dizi[1] = 21 ≠ 17 → i = 2.
i = 2: dizi[2] = 12 ≠ 17 → i = 3.
i = 3: dizi[3] = 7 ≠ 17 → i = 4.
i = 4: dizi[4] = 8 ≠ 17 → i = 5.
i = 5: dizi[5] = 1 ≠ 17 → i = 6.
i = 6: dizi[6] = 3 ≠ 17 → i = 7.
i = 7: dizi[7] = 44 ≠ 17 → i = 8.
i = 8: dizi[8] = 56 ≠ 17 → i = 9.
i = 9: dizi[9] = 18 ≠ 17 → i = 10.
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
i = 0 ile başlanır. Aranan: 1 (ortalama durum örneği).
i < n ve dizi[0] = 33 ≠ 1 → döngü gövdesi çalışır.
i++ → i = 1.
i < n ve dizi[1] = 21 ≠ 1 → döngü gövdesi çalışır.
i++ → i = 2.
i < n ve dizi[2] = 12 ≠ 1 → döngü gövdesi çalışır.
i++ → i = 3.
i < n ve dizi[3] = 7 ≠ 1 → döngü gövdesi çalışır.
i++ → i = 4.
i < n ve dizi[4] = 8 ≠ 1 → döngü gövdesi çalışır.
i++ → i = 5.
dizi[5] = 1 = aranan → while koşulu yanlış, döngüden çıkılır.
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
| Durum | Karşılaştırma | Mertebe | Örnek (n = 10) |
|---|---|---|---|
| En iyi (ilk eleman) | 1 | O(1) | 33 → 1 karşılaştırma |
| Ortalama (başarılı, eşit olasılık) | (1 + 2 + … + n) / n = (n + 1)/2 | O(n) | ortalama 5,5 |
| En kötü / başarısız | n | O(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 < nvedizi[i] != aranan. - Aranan değer son hücreye gözcü (sentinel) olarak yazılırsa döngü mutlaka durur;
i < ntestine gerek kalmaz. - Karşılaştırma sayısı ≈ 2n'den ≈ n + 2'ye iner; mertebe yine O(n)'dir.
İ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.
İ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.
| n | Doğrusal (en kötü) | İkili (en kötü) |
|---|---|---|
| 10 | 10 | 4 |
| 1 000 | 1 000 | 10 |
| 10⁶ | 10⁶ | 20 |
| 10⁹ | 10⁹ | 30 |
İ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; }
basveson, arama yapılacak aralığın alt ve üst sınırlarıdır.while,bas ≤ sonolduğ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
ortadö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ı
Sıralı dizi: 15 eleman. Bu diziden en iyi (dengeli) ikili arama ağacını kuralım.
Ortadaki eleman 21 kök olur: ikili aramanın ilk karşılaştırması daima budur.
Sol yarının ortası 14, sağ yarının ortası 28: 21'den küçükler solda, büyükler sağda.
Her alt aralığın ortası bir sonraki düzeyi oluşturur: 11, 18, 25, 32.
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
İ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.
orta = 7 → dizi[7] = 14; 20 > 14 → sağa.
orta = 11 → dizi[11] = 22; 20 < 22 → sola.
orta = 9 → dizi[9] = 18; 20 > 18 → sağa.
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
En iyi durum: 23 aranır. bas = 0, son = 8.
orta = (0 + 8) / 2 = 4 → dizi[4] = 23 = aranan: 1 adımda bulundu, 4 döner.
En kötü durum: 54 aranır (dizide yok).
Adım 1: orta = 4 → 23 < 54 → bas = 5; sol yarı elenir.
Adım 2: orta = (5 + 8) / 2 = 6 → 55 > 54 → son = 5.
Adım 3: orta = (5 + 5) / 2 = 5 → 34 < 54 → bas = 6.
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
Aranan 47; bas = 0, son = 8.
bas ≤ son → orta = (0 + 8) / 2 = 4; dizi[4] = 14.
47 > 14 → bas = 5; sol kısım elenir.
bas ≤ son → orta = (5 + 8) / 2 = 6; dizi[6] = 45.
47 > 45 → bas = 7; sol kısım elenir.
bas ≤ son → orta = (7 + 8) / 2 = 7; dizi[7] = 47.
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.
| Durum | Mertebe |
|---|---|
| En iyi (ilk orta eşleşir) | O(1) |
| Ortalama | O(log n) |
| En kötü / başarısız | O(log n) |
| Ön hazırlık (sıralama) | O(n log n), bir kez |
İkili Arama
İkili Arama: Uygulama Ayrıntıları
(bas + son) / 2, bas + son > 2³¹ − 1 olduğunda negatif olur. Güvenli yazım: bas + (son - bas) / 2 ya da (bas + son) >>> 1.son = orta ya da bas = orta yazmak aralığı küçültmeyebilir → sonsuz döngü. Orta eleman zaten denetlendiği için ±1 şarttır.son = orta − 1 ile sola devam edilir.Arrays.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.
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 }
| anahtar | karakter kodları | toplam % 10 |
|---|---|---|
| "ali" | 97 + 108 + 105 = 310 | 0 |
| "ila" | 105 + 108 + 97 = 310 | 0 |
| "veli" | 118 + 101 + 108 + 105 = 432 | 2 |
- 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ı:
tabloanahtarları 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;
% mile sondan başa dönülür (döngüsel arama). - Boş kovaya varılırsa anahtar yoktur.
Hash ile Arama · Adım adım
Doğrusal Yoklama ile Ekleme
Boş tablo (m = 10). Anahtarlar sırayla eklenecek: 16, 12, 45, 29, 6, 122, 5, 58, 75; h(x) = x % 10.
h(16) = 6: kova boş → 16 buraya yerleşir.
h(12) = 2: kova boş → 12 buraya yerleşir.
h(45) = 5: kova boş → 45 buraya yerleşir.
h(29) = 9: kova boş → 29 buraya yerleşir.
h(6) = 6 dolu → 7 yoklanır; ilk boş kova 7. (2 yoklama)
h(122) = 2 dolu → 3 yoklanır; ilk boş kova 3. (2 yoklama)
h(5) = 5 dolu → 6, 7, 8 yoklanır; ilk boş kova 8. (4 yoklama)
h(58) = 8 dolu → 9, 0 yoklanır; ilk boş kova 0. (3 yoklama)
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
m = 10, indis = 75 % 10 = 5.
tablo[5] = 45 ≠ 75 → indis = (5 + 1) % 10 = 6.
tablo[6] = 16 ≠ 75 → indis = (6 + 1) % 10 = 7.
tablo[7] = 6 ≠ 75 → indis = (7 + 1) % 10 = 8.
tablo[8] = 5 ≠ 75 → indis = (8 + 1) % 10 = 9.
tablo[9] = 29 ≠ 75 → indis = (9 + 1) % 10 = 0. Sondan başa dönüldü.
tablo[0] = 58 ≠ 75 → indis = (0 + 1) % 10 = 1.
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
Ayrı zincirleme (separate chaining): her kova bir bağlı listenin başını tutar. Aynı anahtarlar, h(x) = x % 10.
h(16) = 6 → kova 6 boş; 16 listenin sonuna eklenir.
h(12) = 2 → kova 2 boş; 12 listenin sonuna eklenir.
h(45) = 5 → kova 5 boş; 45 listenin sonuna eklenir.
h(29) = 9 → kova 9 boş; 29 listenin sonuna eklenir.
h(6) = 6 → kova 6 dolu (çakışma); 6 listenin sonuna eklenir.
h(122) = 2 → kova 2 dolu (çakışma); 122 listenin sonuna eklenir.
h(5) = 5 → kova 5 dolu (çakışma); 5 listenin sonuna eklenir.
h(58) = 8 → kova 8 boş; 58 listenin sonuna eklenir.
h(75) = 5 → kova 5 dolu (çakışma); 75 listenin sonuna eklenir.
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öntem | Başarılı arama (ort.) | Başarısız arama (ort.) | α = 0,5 | α = 0,9 |
|---|---|---|---|---|
| Zincirleme | ≈ 1 + α/2 | ≈ 1 + α | 1,25 / 1,5 | 1,45 / 1,9 |
| Doğrusal yoklama | ≈ ½ (1 + 1/(1 − α)) | ≈ ½ (1 + 1/(1 − α)²) | 1,5 / 2,5 | 5,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])
İ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; }
basvesonarama 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
indishesaplanır ve kontrol edilir:- eşitse indis döner;
dizi[indis]küçükseindis + 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
Aranan 24. Çubuk yüksekliği değeri gösterir: dağılım yaklaşık düzgündür.
indis = 0 + (24 − 10)·14 / (47 − 10) = 0 + 196/37 → 5; dizi[5] = 19.
19 < 24 → bas = 6.
indis = 6 + (24 − 20)·8 / (47 − 20) = 6 + 32/27 → 7; dizi[7] = 21.
21 < 24 → bas = 8.
indis = 8 + (24 − 22)·6 / (47 − 22) = 8 + 12/25 → 8; dizi[8] = 22.
22 < 24 → bas = 9.
indis = 9 + (24 − 23)·5 / (47 − 23) = 9 + 5/24 → 9; dizi[9] = 23.
23 < 24 → bas = 10.
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
| Durum | Mertebe |
|---|---|
| En iyi | O(1) |
| Ortalama (düzgün dağılım) | O(log log n) |
| En kötü (çarpık dağılım) | O(n) |
| Ek bellek | O(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ğerlerdeint'i taşırabilir;longkullanın.
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.
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;konumbaş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
Aranan 69; n = 10 → blok = ⌊√10⌋ = 3; atlama = 3, konum = 0.
dizi[min(3, 10) − 1] = dizi[2] = 30 < 69 → bir blok atla: konum = 3, atlama = 6.
dizi[min(6, 10) − 1] = dizi[5] = 47 < 69 → bir blok atla: konum = 6, atlama = 9.
dizi[8] = 79 ≥ 69 → atlama biter. Aranan, [6, 9) bloğunda olabilir.
Doğrusal arama: dizi[6] = 61 < 69 → konum = 7.
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.
| n | m = √n | ≈ 2√n − 1 | ikili |
|---|---|---|---|
| 100 | 10 | 19 | 7 |
| 10 000 | 100 | 199 | 14 |
| 10⁶ | 1 000 | 1 999 | 20 |
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.
Ü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.
Ü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; }
konumbaşta 1'dir;dizi[konum] ≤ arananoldukça 2 ile çarpılır.- Döngü sonunda
konum, aranılan değerin olabileceği aralığın sonunu işaret eder;konum / 2başı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.
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
Aranan 47. Önce dizi[0] = 2 ≠ 47.
dizi[1] = 9 ≤ 47 → konum = 2.
dizi[2] = 30 ≤ 47 → konum = 4.
dizi[4] = 38 ≤ 47 → konum = 8.
dizi[8] = 79 > 47 → katlama biter. Aranan [8/2, min(8, 9)] = [4, 8] aralığındadır.
İkili arama: orta = (4 + 8) / 2 = 6 → dizi[6] = 61 > 47 → son = 5.
İkili arama: orta = (4 + 5) / 2 = 4 → dizi[4] = 38 < 47 → bas = 5.
İ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 | ≈ 7 | 20 |
| 1 000 | ≈ 20 | 20 |
| 500 000 | ≈ 38 | 20 |
Karşılaştırma
Arama Algoritmalarının Karşılaştırılması
| Algoritma | Önkoşul | En iyi | Ortalama | En kötü | Ek bellek |
|---|---|---|---|---|---|
| Doğrusal | yok | O(1) | O(n) | O(n) | O(1) |
| İkili | sıralı | O(1) | O(log n) | O(log n) | O(1) |
| Hash (tablo) | hash tablosu | O(1) | O(1) | O(n) | O(m) |
| Ara değer | sıralı, düzgün | O(1) | O(log log n) | O(n) | O(1) |
| Atlayarak | sıralı | O(1) | O(√n) | O(√n) | O(1) |
| Üstel | sı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).
| aranan | Doğrusal | İkili | Atlayarak | Ara değer | Üstel | sonuç |
|---|---|---|---|---|---|---|
| 69 | 8 (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 |
| 47 | 6 (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 |
| 2 | 1 (0) | 3 (4,1,0) | 2 (2,0) | 1 (0) | 1 (0) | 0 |
| 50 | 10 (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 |
Karşılaştırma
Hangi Algoritma Ne Zaman?
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
| Konu | Hatırlanacaklar |
|---|---|
| Doğrusal | baştan sona tek tek karşılaştır; önkoşul yok; ortalama (n + 1)/2, en kötü n → O(n) |
| İkili | sıralı dizi; orta ile karşılaştır, yarıyı ele; en fazla ⌊log₂ n⌋ + 1 → O(log n) |
| Hash | yeri hesapla: h(anahtar); çakışma → doğrusal yoklama ya da zincirleme; ort. O(1), en kötü O(n) |
| Ara değer | konumu 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) |
| Üstel | sınırı 1, 2, 4, … katla, [konum/2, konum]'da ikili arama → O(log i) |
Özet
Etkileşimli Simülatörler
Özet
Sık Yapılan Hatalar
while (bas < son) tek elemanlı aralığı atlar; son = orta sonsuz döngüye sokabilir.(bas + son) / 2 taşar; bas + (son − bas) / 2 yazın.dizi[son] == dizi[bas] ise sıfıra bölme; aranan uç değerlerin dışındaysa indis dizi dışına çıkar. Önce aralığı denetleyin.Math.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).
- İkili aramada 65 için hangi indisler yoklanır?
- Atlayarak aramada 65 için yoklanan indisler nelerdir?
- Ara değer arama 65 ve 30'u kaç yoklamada sonuçlandırır?
- Üstel aramada 65 için yoklanan indisler nelerdir?
- 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
- orta: 7, 11, 9, 10 → indis 10 (4 yoklama).
- blok = 4: blok sonları 3, 7, 11, sonra doğrusal 8, 9, 10 → indis 10 (6 yoklama).
- 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.
- 0; 1, 2, 4, 8 (52 ≤ 65), 16 ≥ n → aralık [8, 15]; orta 11, 9, 10 → indis 10 (8 yoklama).
- [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
- İkili aramanın özyinelemeli sürümünü yazın; ek bellek karmaşıklığı nedir?
- Tekrarlı elemanlar içeren sıralı dizide aranan değerin ilk konumunu bulan ikili arama yazın.
- Başarılı doğrusal aramada ortalama karşılaştırma sayısının (n + 1)/2 olduğunu gösterin.
- Atlayarak aramada en iyi blok boyunun √n olduğunu gösterin; n = 10 000 için en kötü yoklama sayısı?
- 10, 20, …, 100 dizisinde ara değer arama 70'i kaç yoklamada bulur? 1, 2, …, 9, 1000 dizisinde 9'u?
- 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ı
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).- Eşitlikte
sonuc = orta; son = orta − 1;ile sola devam edin; döngü sonundasonucdöner. Hâlâ O(log n). - i. eleman i karşılaştırmayla bulunur, olasılık 1/n: Σ i/n = n(n + 1)/(2n) = (n + 1)/2.
- f(m) = n/m + m − 1, f′(m) = −n/m² + 1 = 0 ⇒ m = √n. n = 10 000: 100 + 99 = 199.
- 70: indis = 0 + 60·9/90 = 6 → 1 yoklama. 9: indisler 0, 1, …, 8 → 9 yoklama (çarpık dağılım).
- 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.