Arama ve sıralama: linear, binary, selection, insertion, merge sort
İki arama ve üç sıralama algoritması, her geçişten sonra dizinin hali ve Big-O karşılaştırması. Sınavda 'pass 2'den sonra dizi ne olur' soruları gelir.
Arama (searching) ve sıralama (sorting), algoritma analizinin ilk durağı. Bu hafta iki arama ve üç sıralama algoritmasını adım adım izliyorsun. Sınavda tipik soru: “bu diziye selection sort uygulanınca ikinci geçişten sonra dizi ne olur”, “binary search hangi indekslere bakar”, ve “Big-O’su nedir, neden”. Kodları ezberlemek yerine her algoritmanın tek cümlelik fikrini bil; kod o fikirden çıkar.
Altın kurallar:
- Binary search sadece sıralı dizide çalışır. Her adımda aralığı yarıya böler.
- Selection sort: kalanın en küçüğünü bul, başa koy.
- Insertion sort: sıradaki elemanı soldaki sıralı kısımda yerine kaydır.
- Merge sort: ikiye böl, iki yarıyı sırala, birleştir.
- Big-O: linear , binary , selection ve insertion , merge .
Arama
Linear search
Diziyi baştan sona tek tek gez, aranan değeri görünce indeksini döndür. Dizi sıralı olmak zorunda değil.
static int linearSearch(int[] a, int key) {
for (int i = 0; i < a.length; i++)
if (a[i] == key) return i;
return -1;
}
int[] data = {42, 7, 19, 3, 25, 7};
Karşılaştırma sayılarını da sayarak üç arama:
1 comps=2
4 comps=5
-1 comps=6
(7 aranınca ilk 7 bulunur, indeks 1; 25 için 5 karşılaştırma; 99 yok, dizinin hepsine bakıldı.)
Binary search
Dizi sıralıysa ortadaki elemana bak. Aranan daha büyükse sol yarıyı, daha küçükse sağ yarıyı tamamen eleyebilirsin. Geçen hafta özyinelemeli yazdık; döngülü sürümü:
static int binarySearch(int[] a, int key) {
int low = 0, high = a.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (a[mid] == key) return mid;
if (a[mid] < key) low = mid + 1; // sağ yarı
else high = mid - 1; // sol yarı
}
return -1;
}
int[] sorted = {2, 5, 9, 14, 20, 27, 33, 41, 48, 56, 63};
binarySearch(sorted, 41);
binarySearch(sorted, 4);
low=0 high=10 mid=5 a[mid]=27
low=6 high=10 mid=8 a[mid]=48
low=6 high=7 mid=6 a[mid]=33
low=7 high=7 mid=7 a[mid]=41
index 7 comps=4
low=0 high=10 mid=5 a[mid]=27
low=0 high=4 mid=2 a[mid]=9
low=0 high=1 mid=0 a[mid]=2
low=1 high=1 mid=1 a[mid]=5
index -1 comps=4
4 aranırken son adımda low = 1, high = 1, a[1] = 5 > 4 olduğu için high = 0 olur; low > high ve döngü biter: −1.
TUZAK ·
low < high. Koşulu<yazarsan aralık tek elemana indiğinde o eleman hiç kontrol edilmez.{2, 5, 9}dizisinde bu hatalı sürüm 5’i buldu ama 9 ve 2 için −1 döndü.
Java’nın hazır sürümü: Arrays.binarySearch(sorted, 27) → 5. O da dizinin sıralı olmasını bekler; sıralı değilse sonuç anlamsızdır.
Sıralama
Üç algoritmayı aynı dizi üzerinde izleyeceğiz: {29, 10, 14, 37, 13}.
Selection sort
Fikir: dizinin sıralanmamış kısmındaki en küçük elemanı bul, o kısmın başıyla yer değiştir (swap). Her geçişte (pass) baştan bir eleman daha kesin yerine oturur.
static void selectionSort(int[] a) {
for (int i = 0; i < a.length - 1; i++) {
int min = i;
for (int j = i + 1; j < a.length; j++)
if (a[j] < a[min]) min = j;
int tmp = a[i]; a[i] = a[min]; a[min] = tmp; // swap
}
}
Her geçişten sonra dizi:
pass 1: [10, 29, 14, 37, 13]
pass 2: [10, 13, 14, 37, 29]
pass 3: [10, 13, 14, 37, 29]
pass 4: [10, 13, 14, 29, 37]
comps=10
pass 1: [29 10 14 37 13] en küçük 10 (indeks 1) ↔ indeks 0 → [10 | 29 14 37 13]
pass 2: [10 | 29 14 37 13] en küçük 13 (indeks 4) ↔ indeks 1 → [10 13 | 14 37 29]
pass 3: en küçük 14 zaten indeks 2'de → [10 13 14 | 37 29]
pass 4: en küçük 29 (indeks 4) ↔ indeks 3 → [10 13 14 29 | 37]
Karşılaştırma sayısı her girdide aynı: . n = 5 için 10. Bu yüzden selection sort her durumda .
Insertion sort
Fikir: kartları elinde sıralar gibi. Soldaki kısım her zaman sıralı. Sıradaki elemanı (key) al, kendisinden büyük olanları birer sağa kaydır, açılan boşluğa key’i koy.
static void insertionSort(int[] a) {
for (int i = 1; i < a.length; i++) {
int key = a[i];
int j = i - 1;
while (j >= 0 && a[j] > key) {
a[j + 1] = a[j]; // sağa kaydır
j--;
}
a[j + 1] = key;
}
}
pass 1: [10, 29, 14, 37, 13]
pass 2: [10, 14, 29, 37, 13]
pass 3: [10, 14, 29, 37, 13]
pass 4: [10, 13, 14, 29, 37]
comps=8
İki algoritmanın geçişlerini karşılaştır: selection sort’ta baştaki elemanlar kesin yerindedir (pass 2’den sonra 10 ve 13 bir daha değişmez). Insertion sort’ta baştaki kısım sıralıdır ama kesin değildir: pass 3’te [10, 14, 29, 37] sıralı ama 13 daha sonra araya girdi.
Insertion sort’un karşılaştırma sayısı girdiye bağlı:
| Girdi | Insertion | Selection |
|---|---|---|
{1, 2, 3, 4, 5} (sıralı) | 4 | 10 |
{5, 4, 3, 2, 1} (ters) | 10 | 10 |
{29, 10, 14, 37, 13} | 8 | 10 |
Sıralı girdide insertion sort her eleman için bir karşılaştırma yapıp durur: en iyi durum . Ters sıralıda her eleman başa kadar kayar: en kötü durum .
Merge sort
Fikir: böl ve yönet (divide and conquer). Diziyi ikiye böl, iki yarıyı özyinelemeli olarak sırala, sonra iki sıralı yarıyı tek sıralı diziye birleştir (merge). Tek elemanlı dizi zaten sıralıdır: base case.
static void mergeSort(int[] a, int low, int high) {
if (low >= high) return; // 0 ya da 1 eleman
int mid = (low + high) / 2;
mergeSort(a, low, mid); // sol yarı
mergeSort(a, mid + 1, high); // sağ yarı
merge(a, low, mid, high); // birleştir
}
static void merge(int[] a, int low, int mid, int high) {
int[] tmp = new int[high - low + 1];
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= high)
tmp[k++] = (a[i] <= a[j]) ? a[i++] : a[j++]; // küçük olanı al
while (i <= mid) tmp[k++] = a[i++]; // kalanlar
while (j <= high) tmp[k++] = a[j++];
System.arraycopy(tmp, 0, a, low, tmp.length);
}
{38, 27, 43, 3, 9, 82, 10} için her merge’den sonra o aralığın hali:
merge [0..1]: [27, 38]
merge [2..3]: [3, 43]
merge [0..3]: [3, 27, 38, 43]
merge [4..5]: [9, 82]
merge [4..6]: [9, 10, 82]
merge [0..6]: [3, 9, 10, 27, 38, 43, 82]
[3, 9, 10, 27, 38, 43, 82] comps=14
[38 27 43 3 9 82 10]
[38 27 43 3] [9 82 10]
[38 27] [43 3] [9 82] [10]
[38] [27] [43] [3] [9] [82]
└─┬─┘ └─┬─┘ └─┬─┘
[27 38] [3 43] [9 82]
└────┬────┘ └──┬──┘
[3 27 38 43] [9 10 82]
└──────────┬──────────┘
[3 9 10 27 38 43 82]
Bölme seviye sürer, her seviyede toplam n eleman birleştirilir: , girdiden bağımsız. Bedeli: geçici dizi için ek bellek.
Big-O karşılaştırması
| Algoritma | En iyi | Ortalama | En kötü | Ek bellek | Not |
|---|---|---|---|---|---|
| Linear search | yok | Sırasız dizide de çalışır | |||
| Binary search | yok | Sıralı dizi şart | |||
| Selection sort | yok | En fazla n − 1 swap | |||
| Insertion sort | yok | Neredeyse sıralı veride hızlı, stable | |||
| Merge sort | Büyük veride tercih |
Teorinin pratikteki karşılığı: ters sıralı dizilerde sayılan karşılaştırmalar.
| n | Selection sort | Merge sort |
|---|---|---|
| 1000 | 499500 | 4932 |
| 2000 | 1999000 | 10864 |
| 4000 | 7998000 | 23728 |
n her iki katına çıktığında selection sort’un işi dört katına çıkıyor (), merge sort’unki iki kattan biraz fazla ().
İPUCU · gerçek hayatta. Kendi sıralamanı yazmazsın:
Arrays.sort(dizi)veCollections.sort(liste)hazır ve . Sınavda ise algoritmayı elle izlemen ve yazman istenir; hazır metot kabul edilmez.
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
| Binary search her dizide çalışır | Sıralı dizi şart |
while (low < high) yeterli | <= olmalı, yoksa tek kalan eleman atlanır |
| Selection sort sıralı dizide hızlıdır | Karşılaştırma sayısı her zaman n(n−1)/2 |
| Insertion sort ile selection sort’un pass’leri aynı | Selection baştan kesinleştirir; insertion baştaki kısmı sıralı tutar ama araya eleman girebilir |
| Merge sort önce sağ yarıyı birleştirir | Sol yarı tamamen biter, sonra sağ |
| Merge sort ek bellek istemez | Geçici dizi: O(n) |
| O(n log n) ile O(n²) arasında küçük fark var | n = 4000’de 23728’e karşı 7998000 |
| n geçiş gerekir | n − 1 geçiş yeter, son eleman kendiliğinden yerinde |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
Show the array after each pass of selection sort (smallest to the front).
int[] a = {29, 10, 14, 37, 13};Cevabı göster
Her geçişte sıralanmamış kısmın en küçüğü bulunur ve kısmın başıyla yer değiştirir. Pass 1: en küçük 10, 29 ile değişir. Pass 2: kalan [29,14,37,13] içinde en küçük 13, 29 ile değişir. Pass 3: kalan [14,37,29], en küçük 14 zaten yerinde (kendisiyle değişir, dizi aynı). Pass 4: [37,29] → 29 öne. n = 5 için 4 geçiş.
Show the array after each pass of insertion sort.
int[] a = {29, 10, 14, 37, 13};Cevabı göster
Her geçişte bir sonraki eleman, soldaki sıralı kısımda doğru yerine kaydırılarak yerleşir. Pass 1: 10, 29'un önüne. Pass 2: 14, 29'un önüne (10'dan büyük). Pass 3: 37 zaten en büyük, değişmez. Pass 4: 13 üç elemanı sağa kaydırıp 10'dan sonraya girer.
Binary search for 41 in {2, 5, 9, 14, 20, 27, 33, 41, 48, 56, 63} with mid = (low + high) / 2. List the mid values checked.
Cevabı göster
(0,10) → mid 5, a[5] = 27 < 41, low = 6. (6,10) → mid 8, a[8] = 48 > 41, high = 7. (6,7) → mid 6, a[6] = 33 < 41, low = 7. (7,7) → mid 7, a[7] = 41: bulundu. 4 karşılaştırma; 11 elemanlı dizide en fazla 4 gerekir.
How many comparisons does linear search make to find 25 in {42, 7, 19, 3, 25, 7}, and to search for 99?
Cevabı göster
Linear search baştan tek tek bakar: 42, 7, 19, 3, 25 → 5. karşılaştırmada bulur (indeks 4). Olmayan bir değer için dizinin hepsine bakar: 6 karşılaştırma, −1 döner. 7 aransaydı ilk 7'yi (indeks 1) bulup dururdu: 2 karşılaştırma.
What is the Big-O (worst case) of each algorithm? linear search, binary search, selection sort, insertion sort, merge sort
Cevabı göster
Linear her elemana bakabilir: . Binary her adımda aralığı yarıya indirir: . Selection ve insertion iç içe iki döngü: . Merge sort seviyede bölünür, her seviyede n eleman birleşir: .
Insertion sort makes 4 comparisons on {1, 2, 3, 4, 5} but 10 on {5, 4, 3, 2, 1}. Selection sort makes 10 on both. Why?
Cevabı göster
Insertion sort sıralı girdide her eleman için tek bir karşılaştırma yapıp durur: n − 1 = 4. Ters sıralı girdide her eleman en başa kadar kayar: 1 + 2 + 3 + 4 = 10. Selection sort en küçüğü bulmak için kalan kısmın hepsine her zaman bakar: n(n − 1)/2 = 10, girdiye bakmaksızın.
Merge sort sorts {38, 27, 43, 3, 9, 82, 10} with mid = (low + high) / 2, left half first. What is the result of the third merge?
Cevabı göster
Merge'ler sırası: [0..1] → [27, 38], [2..3] → [3, 43], sonra bu ikisi [0..3] → [3, 27, 38, 43]. Sol yarı tamamen bitmeden sağ yarıya geçilmez. Sonra [4..5] → [9, 82], [4..6] → [9, 10, 82], en son [0..6] → [3, 9, 10, 27, 38, 43, 82].
Find the bug: the search below misses some elements of {2, 5, 9}.
static int search(int[] a, int key) {
int low = 0, high = a.length - 1;
while (low < high) {
int mid = (low + high) / 2;
if (a[mid] == key) return mid;
if (a[mid] < key) low = mid + 1;
else high = mid - 1;
}
return -1;
}Cevabı göster
Aralık tek elemana inince (low == high) döngü o elemanı kontrol etmeden biter. Denemede 5 bulundu (1) ama 9 ve 2 için −1 döndü. Doğrusu while (low <= high).
Which statement is TRUE?
- Binary search works on any array
- Selection sort does fewer comparisons on an already sorted array
- Merge sort needs an extra array proportional to n
- Insertion sort is O(n log n) in the worst case
Cevabı göster
merge adımı iki yarıyı geçici bir diziye birleştirir: ek bellek . Binary search sadece sıralı dizide çalışır. Selection sort'un karşılaştırma sayısı girdiden bağımsızdır. Insertion sort'un en kötü durumu .
For n = 1000, 2000 and 4000 (reverse-ordered input), selection sort made 499500, 1999000 and 7998000 comparisons; merge sort made 4932, 10864 and 23728. What happens to each when n doubles?
Cevabı göster
: n iki katına çıkınca , dört kat (499500 → 1999000 → 7998000). : , iki kattan biraz fazla (4932 → 10864 → 23728). n büyüdükçe fark uçurum olur; bu yüzden büyük veride merge sort (ve Arrays.sort) kullanılır.
How many comparisons does binary search need at most for a sorted array of 1,000,000 elements?
Cevabı göster
Her karşılaştırma aralığı yarıya böler. , yani en fazla 20 adım. Linear search aynı dizide en kötü 1.000.000 karşılaştırma yapar.
Write a method sortByLength(String[] words) that sorts the array by word length (shortest first) using insertion sort. Words with equal length must keep their original order. {"kiwi", "fig", "banana", "apple", "date"} becomes [fig, kiwi, date, apple, banana].
Cevabı göster
Insertion sort kararlıdır (stable): eşit elemanları kaydırmadığı için sıraları korunur. Bunun için karşılaştırma > olmalı, >= değil:
public static void sortByLength(String[] words) {
for (int i = 1; i < words.length; i++) {
String key = words[i];
int j = i - 1;
while (j >= 0 && words[j].length() > key.length()) {
words[j + 1] = words[j]; // sağa kaydır
j--;
}
words[j + 1] = key;
}
}kiwi ve date ikisi de 4 harfli; kiwi önce geldiği için sonuçta da önce: [fig, kiwi, date, apple, banana].Doğrulama: Bütün izler, karşılaştırma sayıları ve çıktılar JDK 26 ile çalıştırıldı (java verify/SEN1002/W13.java, W13b.java). Büyüme tablosu aynı dosyada n = 1000, 2000, 4000 için sayılan karşılaştırmalardır.