Bölüm 13: Öncelikli Kuyruk ve Heap
Çoktan Seçmeli · Öncelikli Kuyruk
Soru 1: Doğru Yapıyı Seçmek
Bir işletim sistemi, hazır işler arasından her seferinde en kısa sürecek işi çalıştırmak istiyor. İşler sık sık geliyor, “en kısayı ver” isteği de sık. Hangisi en uygundur?
B: Hem ekleme hem “en kısayı al” O(lg n) olur. A yanlış uca öncelik verir. Sıralı dizide yer ikili aramayla O(lg n)'de bulunsa da öğeleri kaydırmak O(n) sürer; sırasız listede ekleme O(1) ama en kısayı bulmak Θ(n)'dir.
Çoktan Seçmeli · Heap Yapısı
Soru 2: İndis Hesabı
50 öğeli, 1 tabanlı dizide tutulan bir heap'te (yigin[1..50]) 25. düğüm için hangisi doğrudur?
A: Ebeveyn 25 / 2 = 12 (tam sayı bölmesi). Çocuklar 2 · 25 = 50 ve 51 olurdu; 51 > n olduğu için yalnızca sol çocuk vardır. Tam ikili ağaçta en çok bir düğüm tek çocukludur: n çift ise n / 2 numaralı düğüm.
Çoktan Seçmeli · Heap Yapısı
Soru 3: Hangisi Maksimum Heap?
Aşağıdaki dizilerden (1 tabanlı, soldan sağa yigin[1..7]) hangisi bir maksimum heap'tir?
D: Her k ≥ 2 için yigin[k/2] ≥ yigin[k]: 90 ≥ 70, 80; 70 ≥ 20, 60; 80 ≥ 75, 10. A'da 4. indisteki 75, ebeveyni 70'ten büyük; B'de 5. indisteki 85, ebeveyni 80'den büyük; C'de 6. indisteki 85, ebeveyni 80'den büyük.
Çoktan Seçmeli · Heap Yapısı
Soru 4: Parantezsiz Makro
Makro parantezsiz tanımlanmıştır. Aşağıdaki kod ne yazdırır?
#define YIGIN_BOSMU(n) !n int n = 5, k = 3; printf("%d\n", YIGIN_BOSMU(n - k));
C: Önişlemci metni olduğu gibi yerleştirir: !n - k olur. ! önce uygulanır: !5 = 0, sonra 0 − 3 = −3. −3 sıfırdan farklı olduğu için koşulda “boş” sayılır, oysa 2 öğe vardır. Doğru tanım (!(n)) ile 0 yazılırdı.
Çıktı Sorusu · Ekleme
Soru 5: Art Arda Ekleme
Boş bir maksimum heap'e maksimum_yigin_ekle ile sırasıyla 12, 7, 19, 3, 15, 25 ekleniyor. Her eklemeden sonra dizi nedir? Toplam kaç kaydırma (yigin[i] = yigin[i / 2]) yapılır?
Cevabı göster
| eklenen | kaydırma | yigin[1..n] |
|---|---|---|
| 12 | 0 | 12 |
| 7 | 0 | 12 7 |
| 19 | 1 (12) | 19 7 12 |
| 3 | 0 | 19 7 12 3 |
| 15 | 1 (7) | 19 15 12 3 7 |
| 25 | 2 (12, 19) | 25 15 19 3 7 12 |
Toplam 4 kaydırma. 25 en kötü durumdur: köke kadar ⌊lg 6⌋ = 2 düzey çıkar.
Çoktan Seçmeli · Ekleme
Soru 6: Takas mı, Kaydırma mı?
Ekleme sırasında yeni öğe k düzey yukarı çıkıyor. Her adımda ebeveyniyle takas eden sürüm (takas başına 3 atama) ile sunumdaki kaydırma sürümü toplam kaç öğe ataması yapar?
C: Takasta her düzeyde geçici değişkenle 3 atama yapılır: 3k. Kaydırmada her düzeyde ebeveyn bir kez aşağı yazılır (k atama), yeni öğe en sonda bir kez yerine yazılır: k + 1.
Adım Adım Çalıştırma · Ekleme
Soru 7: ekle(55) Adım Adım
yigin[1..7] = 60 45 50 20 40 48 30 olan maksimum heap'e maksimum_yigin_ekle(55) uygulanıyor. Boşluk (?) nasıl ilerler, son dizi nedir? (Düğüm altındaki sayı indistir.)
Başlangıç heap'i.
Dolu değil; i = ++n = 8: tam ağacın ilk boş konumu. item henüz yazılmaz.
55 ≤ 20? Hayır → 20 aşağı kayar: yigin[8] = yigin[4].
i /= 2 → 4: boşluk bir düzey yukarıda.
55 ≤ 45? Hayır → 45 aşağı kayar: yigin[4] = yigin[2]; i = 2.
55 ≤ 60 → break: yer bulundu.
yigin[2] = 55. Dizi: 60 55 50 45 40 48 30 20. 2 kaydırma + 1 yazma.
void maksimum_yigin_ekle(eleman item) { if (YIGIN_DOLUMU(n)) { yigin_dolu(); return; } int i = ++n; while (i != 1) { if (item.x <= yigin[i / 2].x) break; yigin[i] = yigin[i / 2]; i /= 2; } yigin[i] = item; }
Hata Bulma · Ekleme
Soru 8: 0 Tabanlı Dizi, 1 Tabanlı Formül
Kök yigin[0]'da tutulan maksimum heap'e ekleme böyle yazılmıştır. Boş heap'e 1, 4, 5, 6, 2, 3, 7 eklenince dizi ne olur? Heap özelliği korunur mu? Hata nedir?
eleman yigin[16]; /* kök yigin[0] */ int n = 0; void ekle(eleman item) { int i = n++; while (i != 0) { if (item.x <= yigin[i / 2].x) break; yigin[i] = yigin[i / 2]; i /= 2; } yigin[i] = item; }
Cevabı göster
| eklenen | yigin[0..n−1] |
|---|---|
| 1, 4, 5 | 5 4 1 |
| 6 | 6 5 1 4 |
| 2 | 6 5 2 4 1 |
| 3 | 6 5 3 4 1 2 |
| 7 | 7 6 3 5 1 2 4 |
Son dizi heap değildir: 2. indisteki 3'ün çocuğu 6. indisteki 4'tür ve 3 < 4.
0 tabanlı dizide ebeveyn (i − 1) / 2'dir. i / 2 tek i'de doğru sonucu verir, çift i'de bir sonraki düğümü verir. 7, 6 → 3 → 1 → 0 yolunu izleyip 4'ü 3'ün altına iter; doğru yol 6 → 2 → 0'dır ve sonuç 7 5 6 1 2 3 4 olur.
Adım Adım Çalıştırma · Silme
Soru 9: al() Adım Adım
Önceki ekleme sorusunda oluşan 60 55 50 45 40 48 30 20 heap'ine maksimum_yigin_al(&e) uygulanıyor. Hangi öğeler yukarı alınır, son dizi nedir?
Kök (60) alınır; sondaki 20 çıkarılır, ona kökten yer aranır. n = 7.
Çocuklar 55 ve 50: büyüğü j = 2. 20 ≥ 55? Hayır.
55 yukarı alınır; boşluk 2. konuma iner.
Çocuklar 45 ve 40: büyüğü 45; 20 ≥ 45? Hayır → 45 yukarı, boşluk 4. konuma.
j = 8 > n = 7: i yaprak. yigin[4] = 20. Dizi: 55 45 50 20 40 48 30; e = 60.
bool maksimum_yigin_al(eleman *cikan) { if (YIGIN_BOSMU(n)) return false; *cikan = yigin[1]; eleman deger = yigin[n--]; int i = 1, j = 2; while (j <= n) { if (j < n && yigin[j].x < yigin[j + 1].x) j++; if (deger.x >= yigin[j].x) break; yigin[i] = yigin[j]; i = j; j *= 2; } yigin[i] = deger; return true; }
Hata Bulma · Silme
Soru 10: Yanlış Çocuğu Seçmek
maksimum_yigin_al'da çocuk seçme satırındaki karşılaştırma ters yazılmıştır. 60 55 50 45 40 48 30 20 heap'ine bu yordam iki kez uygulanırsa ne olur?
if (j < n && yigin[j].x > yigin[j + 1].x) j++; /* hatalı: küçük çocuk */ if (deger.x >= yigin[j].x) break; yigin[i] = yigin[j]; i = j; j *= 2;
Cevabı göster
- 1. çağrı: 60 doğru alınır. Kökte çocuklar 55 ve 50'dir; küçüğü (50) seçilir ve yukarı çıkar. Ardından 50'nin yerindeki çocuklardan küçüğü 30 seçilir. Dizi: 50 55 30 45 40 48 20.
- Heap bozuldu: kökteki 50, çocuğu 55'ten küçük; 3. indisteki 30, çocuğu 48'den küçük.
- 2. çağrı kökteki 50'yi döndürür; oysa en büyük 55'tir. Öncelikli kuyruk yanlış öğe verir.
Düzeltme: yigin[j].x < yigin[j + 1].x. Üç değerin (iki çocuk, deger) en büyüğü yukarı çıkmalıdır.
Çoktan Seçmeli · Silme
Soru 11: 4. İndisteki Öğe
Değerleri farklı, en az 8 öğeli bir maksimum heap'te 4. indisteki öğe için hangisi kesinlikle doğrudur?
A: Heap yalnızca ebeveyn ≥ çocuk ilişkisini garanti eder: yigin[4] < yigin[2] < yigin[1]. Kardeşler arasında sıra yoktur; 4. indisteki öğe ikinci büyük bile olmayabilir, yedinci büyük de olabilir. n ≥ 8 olduğu için 8. indis onun çocuğudur, yani yaprak da değildir.
Kod Yazma · Silme
Soru 12: Önceliği Azaltmak
Sunumdaki oncelik_artir'ın tersini yazın: void oncelik_azalt(int k, int yeni), maksimum heap'te k konumundaki öğenin önceliğini yeni (daha küçük) değerine indirip heap'i düzeltsin. 60 55 50 45 40 48 30 20 heap'inde oncelik_azalt(2, 10) sonrası dizi ne olur?
Cevabı göster
void oncelik_azalt(int k, int yeni) { eleman e = yigin[k]; e.x = yeni; int j = 2 * k; while (j <= n) { /* aşağı kaydır */ if (j < n && yigin[j].x < yigin[j + 1].x) j++; /* büyük çocuk */ if (e.x >= yigin[j].x) break; yigin[k] = yigin[j]; /* çocuğu yukarı al */ k = j; j *= 2; } yigin[k] = e; }
Azalan öncelik ancak aşağıda ihlal yaratabilir; silmedeki aşağı kaydırma k'dan başlatılır: O(lg n). Örnekte 45 ve 20 birer düzey yukarı alınır, 10 8. konuma iner → 60 45 50 20 40 48 30 10.
Klasik Soru · Karşılaştırma
Soru 13: Sıralı Liste mi, Heap mi?
Bir öncelikli kuyrukta 1.048.576 (220) öğe var. Üzerinde 1000 ekleme ve 1000 “en büyüğü al” yapılacak. (a) Büyükten küçüğe sıralı bağlı listede, (b) maksimum heap'te en kötü durumda yaklaşık kaç adım sürer? (Bir karşılaştırma ya da kaydırma bir adım.)
Cevabı göster
| 1 ekleme | 1 alma | toplam (1000 + 1000) | |
|---|---|---|---|
| sıralı liste | ≈ 1.048.576 (yer aranır) | 1 (baştan) | ≈ 1,05 milyar |
| heap | ≤ 20 düzey | ≤ 20 düzey × 2 karş. | ≈ 20.000 + 40.000 = 60.000 |
lg 220 = 20 ve n en çok 1.049.576'ya çıkar: yükseklik değişmez. Heap yaklaşık 17.000 kat az iş yapar. Alma sayısı ekleme sayısına göre çok küçük olsaydı bile sıralı listede maliyeti eklemeler belirlerdi.
Çoktan Seçmeli · Karşılaştırma
Soru 14: Maliyet Tablosu
Öncelikli kuyruğu gerçekleyen yapılarla ilgili ifadelerden hangisi yanlıştır?
B yanlıştır: heap yalnızca ebeveyn–çocuk sırası tutar; aranan değer hangi alt ağaçta olabilir bilinmez, en kötü durumda bütün dizi taranır: O(n). Heap bir arama yapısı değildir. C'de en büyük dizinin sonundadır, yalnızca n azaltılır.
Adım Adım Çalıştırma · İleri Konular
Soru 15: yigin_kur Adım Adım
a[1..8] = 4 10 3 5 1 8 9 2 dizisine yigin_kur(a, 8) uygulanıyor. Her k için ne olur, son dizi nedir, kaç taşıma (a[i] = a[j]) yapılır?
Henüz heap değil. Yapraklar (5–8) zaten tek öğelik heap'tir; k = n / 2 = 4'ten başlanır.
k = 4: tek çocuk 2 (j = 8 = n, sağ çocuk yok). 5 ≥ 2 → yerinde kalır.
k = 3: çocuklar 8 ve 9 → büyüğü 9 (j = 7). 3 < 9 → 9 yukarı.
j = 14 > n: a[7] = 3. 3 köklü alt ağaç heap oldu.
k = 2: çocuklar 5 ve 1; 10 ≥ 5 → değişiklik yok.
k = 1: çocuklar 10 ve 9 → 10; 4 < 10 → 10 köke.
i = 2: çocuklar 5 ve 1 → 5; 4 < 5 → 5 yukarı.
i = 4: tek çocuk 2; 4 ≥ 2 → a[4] = 4. Sonuç 10 5 9 4 1 8 3 2, 3 taşıma.
void yigin_kur(eleman a[], int n) { for (int k = n / 2; k >= 1; k--) { eleman deger = a[k]; int i = k, j = 2 * k; while (j <= n) { if (j < n && a[j].x < a[j + 1].x) j++; if (deger.x >= a[j].x) break; a[i] = a[j]; i = j; j *= 2; } a[i] = deger; } }
Klasik Soru · İleri Konular
Soru 16: Artan Dizide Heapify
a[1..15] = 1, 2, …, 15 (artan) dizisine yigin_kur uygulanıyor. Kaç taşıma yapılır? Bu sayı, 15 düğümlü tam ağaç için “inebilecek toplam düzey” üst sınırıyla nasıl ilişkilidir? Dizi azalan sırada (15, 14, …, 1) verilseydi kaç taşıma olurdu?
Cevabı göster
- Artan dizide her iç düğüm kendi alt ağacının en küçüğüdür ve yaprağa kadar iner: 4 düğüm (k = 4..7) 1'er, 2 düğüm (k = 2, 3) 2'şer, kök 3 düzey → 0 · 8 + 1 · 4 + 2 · 2 + 3 · 1 = 11 taşıma.
- Bu, üst sınırın kendisidir: artan dizi yigin_kur için en kötü girdidir. Sınır Σ h · n / 2h+1 < n olduğundan kurma O(n)'dir.
- Sonuç: 15 11 14 9 10 13 7 8 4 2 5 12 6 3 1.
- Azalan dizi zaten bir maksimum heap'tir: her k'da ilk karşılaştırmada
break→ 0 taşıma.
Çoktan Seçmeli · İleri Konular
Soru 17: 4-ary Heap'te İndis
0 tabanlı dizide tutulan 4-ary (her düğümün 4 çocuğu olan) bir heap'te 10. indisteki düğümün ebeveyni ve çocukları hangi indislerdedir?
D: d-ary heap'te (0 tabanlı) ebeveyn (i − 1) / d = 9 / 4 = 2; çocuklar d · i + 1 … d · i + d = 41 … 44. Ağaç daha sığdır (yükseklik ≈ log₄ n), ekleme hızlanır; silmede ise her düzeyde 4 çocuk karşılaştırılır.