Bölüm 8: Öncelikli Kuyruklar ve Heap
Giriş
Öğrenme Çıktıları
Öncelikli kuyruğun temel işlemlerini ve kullanım alanlarını açıklamak.
Heap düzeni ve tam ikili ağaç özelliğini dizi gösterimiyle ilişkilendirmek.
Ekleme, kökü çıkarma ve öbek oluşturma adımlarını uygulamak.
İşlem maliyetlerini ve Heap Sort algoritmasını değerlendirmek.
Öncelikli Kuyruk ADT
Öncelikli Kuyruk (Priority Queue)
- Her öğenin bir önceliği (anahtarı) vardır; öğeler öncelik sırasına göre çıkar.
- Geliş sırası değil, öncelik belirleyicidir: ambulans trafikte öne geçer.
- Öncelikli işlerin yönetiminde kullanılır: görev çizelgeleme, acil servis, olay simülasyonu.
- En yüksek öncelikli öğeye bakmak O(1); onu çıkarmak ise uygulamaya bağlıdır (heap ile O(log n)).

Öncelikli Kuyruk ADT
Temel Kavramlar
Öncelikli Kuyruk ADT
Kuyruk mu, Öncelikli Kuyruk mu?
Acil servise sırasıyla dört hasta geliyor (küçük sayı = daha acil):
| Geliş | 1. | 2. | 3. | 4. |
|---|---|---|---|---|
| Hasta (öncelik) | Ali (3) | Ece (1) | Can (2) | Naz (1) |
| Kuyruk (FIFO) | Ali | Ece | Can | Naz |
| Öncelikli kuyruk | Ece | Naz | Can | Ali |
Öncelikli Kuyruk ADT
Kullanım Alanları
Öncelikli Kuyruk ADT
Temel İşlemler
| İşlem | Anlamı |
|---|---|
insert(k, v) | Ekleme: öğe, önceliğine göre yerini alır. |
removeMin() | Çıkarma: en yüksek öncelikli öğeyi çıkarır ve döndürür. |
min() | Sorgulama (peek): öğeyi döndürür, çıkarmaz. |
size() | Öğe sayısı. |
isEmpty() | Kuyruk boş mu? |
public interface PriorityQueue<K, V> { void insert(K key, V value); Entry<K, V> min(); // boşsa null Entry<K, V> removeMin(); // boşsa null int size(); boolean isEmpty(); }
Max-öncelikli kuyrukta aynı işlemler max() ve removeMax() adını alır. Bu bölümün kod örnekleri, orijinal sunumdaki gibi max-heap (ekle, silMax) kullanır.
Öncelikli Kuyruk ADT
Örnek İşlem Dizisi (min-öncelikli)
| İşlem | Dönen değer | Kuyruğun içeriği |
|---|---|---|
insert(5, A) | { (5,A) } | |
insert(9, C) | { (5,A), (9,C) } | |
insert(3, B) | { (3,B), (5,A), (9,C) } | |
min() | (3,B) | { (3,B), (5,A), (9,C) } |
removeMin() | (3,B) | { (5,A), (9,C) } |
insert(7, D) | { (5,A), (7,D), (9,C) } | |
removeMin() | (5,A) | { (7,D), (9,C) } |
removeMin() | (7,D) | { (9,C) } |
removeMin() | (9,C) | { } |
removeMin() | null | { } boş kuyruk |
Liste ile Uygulama
Sırasız Liste ve Sıralı Liste
insert: sona ekle → O(1)min,removeMin: tüm listeyi tara → O(n)
insert: doğru yeri bul → O(n)min,removeMin: en küçük hep uçta → O(1)
Liste ile Uygulama
Dizi ve Bağlı Liste Temsili
- Öğeler dizide tutulur; sıralı tutmak için ekleme sırasında öğeler kaydırılır (O(n)).
- Öncelikli öğe dizinin başında tutulursa her çıkarmada tüm dizi sola kaydırılır: O(n).
- Doğrusu: en öncelikli öğeyi dizinin sonunda tutmak → çıkarma O(1).
- Öğeler önceliklerine göre listedeki uygun konuma eklenir: yer arama O(n), bağlama O(1).
- Öncelikli öğe listenin başındadır; çıkarma = baştan silme, O(1).
- Kaydırma yok, ama her düğüm ek bir referans taşır.
Liste ile Uygulama
Maliyet Karşılaştırması
| İşlem | Sırasız liste | Sıralı liste | İkili heap |
|---|---|---|---|
size, isEmpty | O(1) | O(1) | O(1) |
insert | O(1) | O(n) | O(log n) |
min | O(n) | O(1) | O(1) |
removeMin | O(n) | O(1) | O(log n) |
| n öğeyle doldurma | O(n) | O(n log n)* | O(n)** |
* Önce sıralayarak. ** Aşağıdan yukarıya heap oluşturma ile (bu bölümün sonunda).
İkili Heap
İkili Heap (Binary Heap)
Öncelikli kuyruğu uygulamak için en yaygın veri yapısıdır. İki koşulu birlikte sağlayan bir ikili ağaçtır:
İkili Heap
Min-Heap ve Max-Heap
anahtar(ebeveyn) ≤ anahtar(çocuk)anahtar(ebeveyn) ≥ anahtar(çocuk)- Sıra yalnızca ebeveyn–çocuk arasında geçerlidir: min-heap'te 7 ile 3 kardeştir, 7 > 3 olması sorun değildir.
- Kökten herhangi bir yaprağa giden her yol sıralıdır (min-heap'te artan, max-heap'te azalan).
İkili Heap
Tam İkili Ağaç (Complete Binary Tree)
son düzey soldan dolu
son düzeyde boşluk var
son düzey sola yaslı değil
İkili Heap
Heap mi, Değil mi?
Her ağaç için karar verin: max-heap mi, min-heap mi, yoksa heap değil mi? Hangi özellik bozuluyor?
Cevapları göster
- Max-heap ✓ (tam ağaç, her ebeveyn ≥ çocukları).
- Heap değil: 12 > ebeveyni 8 → heap-sıra özelliği bozuk.
- Min-heap ✓ (tam ağaç, her ebeveyn ≤ çocukları).
- Heap değil: 8'in çocukları yokken 9'un çocukları var → tam ağaç değil.
İkili Heap
Dizi Gösterimi
- Düğümler kökten başlayarak düzey düzey, soldan sağa (level order) diziye yazılır.
- Tam ağaç olduğu için dizide boşluk kalmaz; işaretçi (referans) gerekmez.
Düğümlerin yanındaki küçük sayılar dizideki indeksleridir. Bu sunumda indeksler 0'dan başlar; kök heap[0]'dadır.
İkili Heap
Ebeveyn ve Çocuk Hesaplamaları
- Java'da tam sayı bölmesi zaten aşağı yuvarlar:
(i - 1) / 2. Çocuk yoksa 2i + 1 ≥ n olur. - 1 tabanlı alternatif (orijinal sunum): heap[0] boş bırakılır; çocuklar 2k, 2k + 1; ebeveyn ⌊k/2⌋. İkisi eşdeğerdir, biri seçilip tutarlı kullanılmalıdır.
Ekleme ve Silme
Öğe Ekleme: Up-Heap (yüzdür, swim)
- Yeni öğe ağacın ilk boş konumuna (son düzeyde en soldaki boş yer = heap[n]) eklenir. Ağaç tam kalır.
- Heap-sıra özelliği bozulmuş olabilir: yeni öğe ebeveyninden büyükse (max-heap) yer değiştirilir.
- Öğe köke ulaşana ya da ebeveyni kendisinden büyük/eşit olana kadar yukarı taşınır.
yuzdur.Ekleme ve Silme
Up-Heap Örneği: ekle(10), ekle(8)
Başlangıç max-heap: her ebeveyn ≥ çocukları. Dizi düzey düzey, soldan sağa tutulur.
ekle(10): 10, ilk boş konuma (indeks 7) eklenir. Ebeveyni: (7−1)/2 = 3 → 2.
10 > 2 → yer değiştir (indeks 7 ↔ 3).
10 > 3 → yer değiştir (indeks 3 ↔ 1).
10 > 9 → yer değiştir (indeks 1 ↔ 0). Kök → dur.
ekle(8): 8, ilk boş konuma (indeks 8) eklenir. Ebeveyni: (8−1)/2 = 3 → 3.
8 > 3 → yer değiştir (indeks 8 ↔ 3). Yeni ebeveyn 9 (indeks 1) ≥ 8 → dur.
Ekleme ve Silme
Boş Heap'e Art Arda Ekleme: 4, 5, 2, 6, 1, 3
ekle(4): heap boş; 4 köke (indeks 0) yerleşir.
ekle(5): 5, ilk boş konuma (indeks 1) eklenir. Ebeveyni: (1−1)/2 = 0 → 4.
5 > 4 → yer değiştir (indeks 1 ↔ 0). Kök → dur.
ekle(2): 2, ilk boş konuma (indeks 2) eklenir. Ebeveyni: (2−1)/2 = 0 → 5. 2 ≤ 5 → yer değiştirme yok.
ekle(6): 6, ilk boş konuma (indeks 3) eklenir. Ebeveyni: (3−1)/2 = 1 → 4.
6 > 4 → yer değiştir (indeks 3 ↔ 1).
6 > 5 → yer değiştir (indeks 1 ↔ 0). Kök → dur.
Dizi dolu → buyut: kapasite 4 → 8. ekle(1): 1, ilk boş konuma (indeks 4) eklenir. Ebeveyni: (4−1)/2 = 1 → 5. 1 ≤ 5 → yer değiştirme yok.
ekle(3): 3, ilk boş konuma (indeks 5) eklenir. Ebeveyni: (5−1)/2 = 2 → 2.
3 > 2 → yer değiştir (indeks 5 ↔ 2). Yeni ebeveyn 6 (indeks 0) ≥ 3 → dur.
Ekleme ve Silme
Max-Heap'e Ekleme: Java Kodu
public class MaxHeap { private int[] heap = new int[4]; private int n = 0; // eleman sayısı public void ekle(int x) { if (n == heap.length) buyut(2 * heap.length); heap[n] = x; // ilk boş konum n++; yuzdur(n - 1); } private void yuzdur(int k) { // up-heap while (k > 0 && heap[(k - 1) / 2] < heap[k]) { yerDegistir(k, (k - 1) / 2); k = (k - 1) / 2; } }
Ekleme ve Silme
En Büyüğü Çıkarma: Down-Heap (batır, sink)
- Kökteki öğe (en büyük) saklanır; döndürülecek değer budur.
- Ağacın son öğesi (heap[n − 1]) köke taşınır, n bir azalır. Ağaç tam kalır.
- Kök artık küçük bir değer taşıyabilir: büyük olan çocukla yer değiştirilir.
- Öğe bir yaprağa ulaşana ya da her iki çocuğundan büyük/eşit olana kadar aşağı taşınır.
Ekleme ve Silme
Down-Heap Örneği: silMax()
silMax(): max = heap[0] = 9 (kök); döndürülecek değer.
Kök ile son öğe yer değiştirir (0 ↔ 6); n = 6, 9 heap'in dışında. Yeni kök 4 özelliği bozabilir → down-heap.
Çocuklar: 3 ve 6 → büyük olan 6. 4 < 6 → yer değiştir (0 ↔ 2).
Tek çocuk 5; 4 < 5 → yer değiştir (2 ↔ 5). İndeks 5 yaprak → dur.
Ekleme ve Silme
Art Arda İki silMax()
silMax() #1: max = heap[0] = 10 (kök); döndürülecek değer.
Kök ile son öğe yer değiştirir (0 ↔ 8); n = 8, 10 heap'in dışında. Yeni kök 3 özelliği bozabilir → down-heap.
Çocuklar: 9 ve 6 → büyük olan 9. 3 < 9 → yer değiştir (0 ↔ 1). 3 ≥ yeni çocukları 2 ve 1 → dur.
silMax() #2: max = heap[0] = 9 (kök); döndürülecek değer.
Kök ile son öğe yer değiştirir (0 ↔ 7); n = 7, 9 heap'in dışında. Yeni kök 0 özelliği bozabilir → down-heap.
Çocuklar: 3 ve 6 → büyük olan 6. 0 < 6 → yer değiştir (0 ↔ 2).
Çocuklar: 5 ve 4 → büyük olan 5. 0 < 5 → yer değiştir (2 ↔ 5). İndeks 5 yaprak → dur.
Ekleme ve Silme
En Büyüğü Çıkarma: Java Kodu
public int silMax() { if (n == 0) throw new NoSuchElementException(); int max = heap[0]; yerDegistir(0, n - 1); // son öğe köke n--; batir(0); if (n > 0 && n == heap.length / 4) // ¼ dolu kucult(heap.length / 2); return max; } private void batir(int k) { // down-heap while (2 * k + 1 < n) { // sol çocuk var mı? int j = 2 * k + 1; if (j + 1 < n && heap[j] < heap[j + 1]) j++; if (heap[k] >= heap[j]) break; yerDegistir(k, j); k = j; } }
Ekleme ve Silme
Min-Heap: insert(1) ve removeMin()
Başlangıç min-heap: her ebeveyn ≤ çocukları; en küçük öğe (2) kökte.
insert(1): 1, ilk boş konuma (indeks 7) eklenir. Ebeveyni: (7−1)/2 = 3 → 9.
1 < 9 → yer değiştir (indeks 7 ↔ 3).
1 < 5 → yer değiştir (indeks 3 ↔ 1).
1 < 2 → yer değiştir (indeks 1 ↔ 0). Kök → dur.
removeMin(): min = heap[0] = 1 (kök); döndürülecek değer.
Kök ile son öğe yer değiştirir (0 ↔ 7); n = 7, 1 heap'in dışında. Yeni kök 9 özelliği bozabilir → down-heap.
Çocuklar: 2 ve 3 → küçük olan 2. 9 > 2 → yer değiştir (0 ↔ 1).
Çocuklar: 5 ve 6 → küçük olan 5. 9 > 5 → yer değiştir (1 ↔ 3). İndeks 3 yaprak → dur.
Ekleme ve Silme
Heap İşlemlerinin Maliyeti
| İşlem | Maliyet | Neden? |
|---|---|---|
size, isEmpty | O(1) | n sayacı tutulur |
max / min (peek) | O(1) | Öğe hep heap[0]'dadır |
ekle (insert) | O(log n)* | Up-heap: yapraktan köke tek yol |
silMax (removeMin) | O(log n)* | Down-heap: kökten yaprağa tek yol |
| Rastgele öğeyi arama | O(n) | Heap sıralı değildir; arama için uygun değil |
* Dizi büyütme/küçültme dahil amortize maliyet. Yükseklik ⌊log₂ n⌋: n = 10⁶ öğede en çok 19 yer değiştirme.
Heap Oluşturma ve Heap Sort
n Öğeden Heap Oluşturma
Yol 1: n kez ekle
- Her ekleme up-heap yapar: O(log n).
- Toplam O(n log n).
- Öğeler tek tek geliyorsa (çevrim içi) tek seçenektir.
Yol 2: aşağıdan yukarıya
- Tüm öğeler baştan elimizdeyse diziyi olduğu gibi al.
- Son iç düğümden (⌊n/2⌋ − 1) köke doğru her düğüme down-heap uygula.
- Toplam O(n).
for (int i = n / 2 - 1; i >= 0; i--) // son iç düğümden köke batir(i); // alt ağaçları heap olan i'yi batır
Heap Oluşturma ve Heap Sort
Aşağıdan Yukarıya Heap Oluşturma
Sıradan bir dizi (heap değil). Yapraklar (indeks 5…9) zaten heap. Son iç düğüm 10/2 − 1 = 4'ten köke doğru her düğüme down-heap.
i = 4: 16 (indeks 4) ≥ çocuku 7 → dur.
i = 3: Çocuklar: 14 ve 8 → büyük olan 14. 2 < 14 → yer değiştir (3 ↔ 7). İndeks 7 yaprak → dur.
i = 2: Çocuklar: 9 ve 10 → büyük olan 10. 3 < 10 → yer değiştir (2 ↔ 6). İndeks 6 yaprak → dur.
i = 1: Çocuklar: 14 ve 16 → büyük olan 16. 1 < 16 → yer değiştir (1 ↔ 4).
i = 1 (devam): Tek çocuk 7; 1 < 7 → yer değiştir (4 ↔ 9). İndeks 9 yaprak → dur.
i = 0: Çocuklar: 16 ve 10 → büyük olan 16. 4 < 16 → yer değiştir (0 ↔ 1).
i = 0 (devam): Çocuklar: 14 ve 7 → büyük olan 14. 4 < 14 → yer değiştir (1 ↔ 3).
i = 0 (devam): Çocuklar: 2 ve 8 → büyük olan 8. 4 < 8 → yer değiştir (3 ↔ 8). İndeks 8 yaprak → dur. Sonuç: max-heap.
Heap Oluşturma ve Heap Sort
Aşağıdan Yukarıya Oluşturma Neden O(n)?
| Düğümün yüksekliği | Düğüm | En çok takas | Toplam |
|---|---|---|---|
| 0 (yapraklar) | 8 | 0 | 0 |
| 1 | 4 | 1 | 4 |
| 2 | 2 | 2 | 4 |
| 3 (kök) | 1 | 3 | 3 |
| n = 15 | 11 |
- Düğümlerin yarısı yapraktır ve hiç iş yapmaz; çok iş yapan (yüksek) düğüm sayısı azdır.
- Genel olarak: Σh (n / 2h+1) · h ≤ n → O(n).
- n kez eklemede ise çok sayıdaki derin düğüm uzun yol yürür: aynı ağaçta en kötü 0 + 2·1 + 4·2 + 8·3 = 34 takas.
Heap Oluşturma ve Heap Sort
Heap Sort (Öbek Sıralaması)
- Dizinin sonunda sıralı kısım büyür, başında heap küçülür; ek dizi gerekmez (yerinde, O(1) ek bellek).
- En kötü durumda bile O(n log n): hızlı sıralamanın (quick sort) O(n²) kötü durumu yoktur.
- Kararlı değildir (stable değil): eşit anahtarların göreli sırası değişebilir.
- Max-heap artan sıralama, min-heap azalan sıralama üretir.
Heap Oluşturma ve Heap Sort
Heap Sort Örneği
Aşama 1 bitti: dizi max-heap. Aşama 2: kökü (en büyük) sona al, heap'i küçült, down-heap.
Kök 6 ile indeks 5'deki 2 yer değiştirir; 6 yerine oturdu, heap boyutu = 5.
Çocuklar: 5 ve 3 → büyük olan 5. 2 < 5 → yer değiştir (0 ↔ 1).
Çocuklar: 4 ve 1 → büyük olan 4. 2 < 4 → yer değiştir (1 ↔ 3). İndeks 3 yaprak → dur.
Kök 5 ile indeks 4'deki 1 yer değiştirir; 5 yerine oturdu, heap boyutu = 4.
Çocuklar: 4 ve 3 → büyük olan 4. 1 < 4 → yer değiştir (0 ↔ 1).
Tek çocuk 2; 1 < 2 → yer değiştir (1 ↔ 3). İndeks 3 yaprak → dur.
Kök 4 ile indeks 3'deki 1 yer değiştirir; 4 yerine oturdu, heap boyutu = 3.
Çocuklar: 2 ve 3 → büyük olan 3. 1 < 3 → yer değiştir (0 ↔ 2). İndeks 2 yaprak → dur.
Kök 3 ile indeks 2'deki 1 yer değiştirir; 3 yerine oturdu, heap boyutu = 2.
Tek çocuk 2; 1 < 2 → yer değiştir (0 ↔ 1). İndeks 1 yaprak → dur.
Kök 2 ile indeks 1'deki 1 yer değiştirir; 2 yerine oturdu, heap boyutu = 1. Dizi sıralı: [1, 2, 3, 4, 5, 6].
Heap Oluşturma ve Heap Sort
Heap Sort: Java Kodu
public static void heapSort(int[] a) { int n = a.length; for (int i = n / 2 - 1; i >= 0; i--) // 1) oluştur batir(a, i, n); for (int son = n - 1; son > 0; son--) { // 2) sırala yerDegistir(a, 0, son); // en büyük sona batir(a, 0, son); // heap boyutu = son } } static void batir(int[] a, int k, int n) { while (2 * k + 1 < n) { int j = 2 * k + 1; if (j + 1 < n && a[j] < a[j + 1]) j++; if (a[k] >= a[j]) break; yerDegistir(a, k, j); k = j; } }
Heap Oluşturma ve Heap Sort
Öncelikli Kuyrukla Sıralama
Genel fikir: tüm öğeleri öncelikli kuyruğa ekle, sonra n kez removeMin ile sırayla çıkar. Hangi sıralamayı elde ettiğimiz uygulamaya bağlıdır:
| Uygulama | n × insert | n × removeMin | Ortaya çıkan algoritma |
|---|---|---|---|
| Sırasız liste | O(n) | O(n²) | Seçmeli sıralama (selection sort), O(n²) |
| Sıralı liste | O(n²) | O(n) | Eklemeli sıralama (insertion sort), O(n²) |
| İkili heap | O(n log n) | O(n log n) | Heap sort, O(n log n) |
Heap Oluşturma ve Heap Sort
Java'da Hazır Sınıf: PriorityQueue
PriorityQueue<Integer> pq = new PriorityQueue<>(); pq.offer(5); pq.offer(1); pq.offer(8); pq.peek(); // 1 (min-heap, O(1)) pq.poll(); // 1 (O(log n)) // max-heap için ters karşılaştırıcı PriorityQueue<Integer> maxPq = new PriorityQueue<>(Comparator.reverseOrder()); // hasta: öncelik küçük olan önce PriorityQueue<Hasta> acil = new PriorityQueue<>( Comparator.comparingInt(h -> h.oncelik));
- İçeride dizi tabanlı ikili min-heap kullanır.
offer/pollO(log n);peek,sizeO(1);remove(nesne)vecontainsO(n).- Thread-safe değildir; çok iş parçacıklı kullanımda
PriorityBlockingQueuetercih edilir. - Eşit öncelikte FIFO garantisi yoktur.
Özet
Özet
| İşlem | Heap | Nasıl? |
|---|---|---|
| peek (max/min) | O(1) | heap[0] |
| ekle | O(log n) | heap[n]'e koy, up-heap |
| silMax / removeMin | O(log n) | son öğeyi köke taşı, down-heap |
| heap oluşturma | O(n) | ⌊n/2⌋ − 1'den 0'a down-heap |
| heap sort | O(n log n) | oluştur + n − 1 kez kökü sona al |
Özet
Etkileşimli Simülatörler
Bu bölümdeki işlemleri kendi değerlerinizle adım adım deneyin:
Özet
Örnek Problemler
- Boş bir max-heap'e sırasıyla 5, 12, 8, 3, 15, 10 ekleyin. Son dizi nedir?
- 1. sorudaki heap'e bir kez
silMax()uygulayın. Dönen değer ve yeni dizi nedir? - {3, 1, 6, 5, 2, 4} dizisini aşağıdan yukarıya yöntemle max-heap'e çevirin.
- 0 tabanlı dizide indeks 13'teki düğümün ebeveyni ve çocukları hangi indekslerdedir?
- {10, 7, 9, 8, 2, 5} bir max-heap midir? Değilse neden?
- Bir min-heap'teki en büyük öğeyi bulmanın maliyeti nedir?
Cevapları göster
- {15, 12, 10, 3, 5, 8}
- 15 döner; yeni dizi {12, 8, 10, 3, 5}.
- {6, 5, 4, 1, 2, 3}
- Ebeveyn ⌊(13 − 1)/2⌋ = 6; çocuklar 27 ve 28.
- Hayır: indeks 1'deki 7'nin çocuğu 8 (indeks 3) ondan büyük.
- O(n): en büyük öğe yapraklardan biridir ve yaklaşık n/2 yaprağın hepsine bakmak gerekir.
Özet
Kaynaklar
- Goodrich, Tamassia ve Goldwasser, Data Structures and Algorithms in Java, 6. baskı, 2014.
- Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, 2022.
- Oracle, Java SE 17
PriorityQueueAPI belgesi.
Son
← → veya boşluk ile gezinin · F: tam ekran · Home/End: ilk/son slayt