Bölüm 11: Öncelikli Kuyruklar ve Heap

Veri Yapıları
Öncelikli kuyruk ADTListe ile uygulamaİkili heap Dizi gösterimiUp-heap / down-heapHeap oluşturmaHeap sortÖncelikli çizelgeleyici
Sercan KÜLCÜ · Giresun Üniversitesi · Bilgisayar Mühendisliği

Giriş

Öğrenme Çıktıları

ADT modeli

Öncelikli kuyruğun temel işlemlerini ve kullanım alanlarını açıklamak.

İkili heap

Heap düzeni ve tam ikili ağaç özelliğini dizi gösterimiyle ilişkilendirmek.

İşlemler

Ekleme, kökü çıkarma ve heap oluşturma adımlarını uygulamak.

Maliyet analizi

İş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.
  • Maliyetler uygulamaya bağlıdır: heap ile en öncelikli öğeye bakmak O(1), onu çıkarmak O(log n); sırasız listede bakmak bile O(n).
112
Önce gelen değil, önceliği yüksek olan geçer.

Öncelikli Kuyruk ADT

Temel Kavramlar

Öğe = (anahtar, değer)Anahtar (key) önceliği, değer (value) asıl veriyi taşır.(1, "kalp krizi") · (4, "burkulma")
ÖncelikAnahtarlar karşılaştırılabilir olmalı (<, ≤). Genelde küçük anahtar = yüksek öncelik (min-öncelikli kuyruk).
En yüksek öncelikKuyruğun "önündeki" öğe: min-kuyrukta en küçük, max-kuyrukta en büyük anahtar.
Eşit öncelikEşit anahtarlı öğeler için FIFO sırası kendiliğinden garanti değildir; gerekiyorsa anahtara bir geliş sıra numarası eklenir.

ADT tanımında Goodrich'teki gibi min-öncelikli kuyruk (removeMin) kullanılır; heap uygulama örnekleri ise max-heap (ekle / silMax) üzerindedir. Hangisinin seçildiği yalnızca karşılaştırma yönünü değiştirir.

Ö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)AliEceCanNaz
Öncelikli kuyrukEceNazCanAli
KuyrukÇıkış sırası = geliş sırası. Önce gelen önce hizmet alır.
Öncelikli kuyrukÇıkış sırası = öncelik sırası. Ece ve Naz eşit (1); burada geliş sırasını koruduk.

Öncelikli Kuyruk ADT

Kullanım Alanları

İşletim SistemleriGörev (süreç) çizelgeleme: yüksek öncelikli süreç işlemciyi önce alır.kesme işleyici > arayüz > yedekleme
Çizge AlgoritmalarıDijkstra en kısa yol ve A* araması, sıradaki en yakın düğümü öncelikli kuyruktan alır.
Acil Durum YönetimiHasta triyajı ve olay yönetimi: en acil vaka önce ele alınır.
Veri SıkıştırmaHuffman kodlaması, her adımda en düşük frekanslı iki düğümü birleştirir.

Öncelikli Kuyruk ADT

Temel İşlemler

İşlemAnlamı
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 heap kod örnekleri max-heap (ekle, silMax) kullanır.

Öncelikli Kuyruk ADT

Örnek İşlem Dizisi (min-öncelikli)

İşlemDönen değerKuyruğ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

Sırasız liste (unsorted)
5 9 3 7 4 min için hepsi taranır ← ekle
  • insert: sona ekle → O(1)
  • min, removeMin: tüm listeyi tara → O(n)
Sıralı liste (sorted)
3 4 5 7 9 min 4 için yer aranır, kaydırılır
  • insert: doğru yeri bul → O(n)
  • min, removeMin: en küçük hep uçta → O(1)
Biri eklemede, diğeri çıkarmada hızlı. İkisini dengeleyen yapı: ikili heap (her ikisi de O(log n)).

Liste ile Uygulama · Kodu adım adım çalıştır

Kod İzleme: sırasız dizide removeMin()

removeMin()n = 5051923374456▲ enk

Sırasız dizi a = [5, 9, 3, 7, 4]: ekleme sona yapıldığı için en küçüğün yeri bilinmez. İlk aday: enk = 0.

removeMin()n = 5051923374456▲ enk▲ k

k = 1: a[1] = 9 ≥ a[0] = 5 → enk değişmez.

removeMin()n = 5051923374456▲ k, enk

k = 2: a[2] = 3 < a[0] = 5 → yeni en küçük, enk = 2.

removeMin()n = 5051923374456▲ enk▲ k

k = 3: a[3] = 7 ≥ a[2] = 3 → enk değişmez.

removeMin()n = 5051923374456▲ enk▲ k

k = 4: a[4] = 4 ≥ a[2] = 3 → enk değişmez.

removeMin()n = 5051923374456▲ enk

k = 5 = n → tarama bitti: 4 karşılaştırma. Her removeMin tüm diziyi tarar → O(n).

removeMin()n = 5051923374456▲ enk

min = a[2] = 3 saklanır.

removeMin()n = 4051924374456▲ enk

Boşluğu kaydırmadan kapatmak için son öğe (4) indis 2'ye taşınır, n = 4. Sıra önemli olmadığından bu O(1).

removeMin()n = 4051924374456

return 3. Kalan dizi [5, 9, 4, 7] yine sırasız.

// sırasız dizi: insert(x) { a[n] = x; n++; }  O(1)
public int removeMin() {        // n > 0 olmalı
  int enk = 0;                  // en küçüğün indisi
  for (int k = 1; k < n; k++)
    if (a[k] < a[enk]) enk = k;
  int min = a[enk];
  a[enk] = a[n - 1];  n--;      // son öğe boşluğa
  return min;
}

Liste ile Uygulama · Kodu adım adım çalıştır

Kod İzleme: sıralı dizide insert(4)

insert(4)n = 5091725334256▲ k

Dizi büyükten küçüğe sıralı; en öncelikli (en küçük) öğe hep sonda. k = n − 1 = 4: sondan başa doğru yer aranır.

insert(4)n = 5091725334256▲ k

a[4] = 2 < 4 → 2, x'ten sonra gelmeli; kaydırılacak.

insert(4)n = 50917253342526▲ k

a[5] = a[4] = 2: bir sağa kaydırıldı (a[4] şimdilik kopya; üzerine yazılacak).

insert(4)n = 50917253342526▲ k

k = 3: bir sola geç.

insert(4)n = 50917253342526▲ k

a[3] = 3 < 4 → 3, x'ten sonra gelmeli; kaydırılacak.

insert(4)n = 50917253343526▲ k

a[4] = a[3] = 3: bir sağa kaydırıldı (a[3] şimdilik kopya; üzerine yazılacak).

insert(4)n = 50917253343526▲ k

k = 2: bir sola geç.

insert(4)n = 50917253343526▲ k

a[2] = 5 ≥ 4 → döngü biter. 4'ün yeri k + 1 = 3.

insert(4)n = 60917253443526

a[3] = 4, n = 6. Dizi yine sıralı. En kötü durumda (x en büyükse) tüm öğeler kayar → O(n).

removeMin()n = 50917253443526▲ dönen

Karşılığında removeMin() çok ucuz: en küçük (2) zaten sonda → n = 5, return 2. Kaydırma yok → O(1).

// sıralı dizi: büyükten küçüğe, min hep a[n-1]
public void insert(int x) {
  int k = n - 1;
  while (k >= 0 && a[k] < x) {    // x'ten küçükler
    a[k + 1] = a[k];              // bir sağa kaydır
    k--;
  }
  a[k + 1] = x;  n++;
}
public int removeMin() { n--; return a[n]; }

Liste ile Uygulama

Dizi ve Bağlı Liste Temsili

Dizi 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).
Bağlı liste temsili
  • Öğ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.
baş35796yeninull▲ onceki

Liste büyükten küçüğe değil, küçükten büyüğe (min-öncelikli): en öncelikli 3 baştadır. Yeni düğüm 6 oluşturuldu.

baş35796yeninull▲ onceki

Yer arama: onceki.sonraki.deger = 7 > 6 olana dek ilerle → onceki = 5. Bu tarama O(n) sürer.

baş35796yeninull▲ onceki

Önce yeni.sonraki = onceki.sonraki (6 → 7). Sıra önemli: önce bu bağ kurulmazsa 7'ye giden yol kaybolur.

baş35796yeninull▲ onceki

Sonra onceki.sonraki = yeni (5 → 6). Bağlama O(1), kaydırma yok; çıkarma her zaman baştan: O(1).

Liste ile Uygulama

Sıralı Dizi ile Öncelikli Kuyruk: Kod

class Oge<T> {
    T veri;
    int oncelik;
    Oge(T v, int p) { veri = v; oncelik = p; }
}
// OncelikliKuyruk<E> alanları
private Oge<E>[] dizi =
    (Oge<E>[]) new Oge[10];
private int boyut = 0;
public void ekle(E veri, int p) {
    if (boyut == dizi.length)
        dizi = Arrays.copyOf(dizi, 2 * boyut);
    int i = boyut;
    while (i > 0 && dizi[i-1].oncelik < p) {
        dizi[i] = dizi[i - 1];  // sağa kaydır
        i--;
    }
    dizi[i] = new Oge<>(veri, p);
    boyut++;
}
public E cikar() {
    if (bosMu()) return null;
    E sonuc = dizi[0].veri;
    for (int i = 1; i < boyut; i++)
        dizi[i - 1] = dizi[i];
    dizi[--boyut] = null;
    return sonuc;
}

Öğeler (veri, öncelik) çifti olarak saklanır; büyük öncelik önde, dizi[0]'da. ekle O(n) (kaydırma), cikar da O(n) (sola kaydırma). Eşit öncelikte < karşılaştırması geliş sırasını korur.

Liste ile Uygulama · Kodu adım adım çalıştır

Kod İzleme: Eşit Öncelikte ekle(D, 5)

0B:51A:22C:1345…

ekle(A, 2), ekle(B, 5), ekle(C, 1) sonrası dizi [B:5, A:2, C:1]. Şimdi ekle(D, 5): iki öğe kayacak.

0B:51A:22C:1345…yeni öğeD:5

ekle(D, 5): boyut 3 == 10? Hayır, büyütme gerekmez.

0B:51A:22C:1345…▲iyeni öğeD:5

i = boyut = 3: ilk boş yuvadan başlayıp sola doğru yer arayacağız.

0B:51A:22C:1345…▲iyeni öğeD:5

i > 0 ve dizi[2].oncelik = 1 < 5 → true: solundaki öğe daha düşük öncelikli.

0B:51A:22C:13C:145…▲iyeni öğeD:5

dizi[3] = dizi[2]: C bir sağa kayar; i = 2.

0B:51A:22C:13C:145…▲iyeni öğeD:5

i > 0 ve dizi[1].oncelik = 2 < 5 → true: solundaki öğe daha düşük öncelikli.

0B:51A:22A:23C:145…▲iyeni öğeD:5

dizi[2] = dizi[1]: A bir sağa kayar; i = 1.

0B:51A:22A:23C:145…▲iyeni öğeD:5

dizi[0].oncelik = 5 < 5 değil (eşit öncelikte önce gelen önde kalır: FIFO) → false, döngü biter.

0B:51D:52A:23C:145…▲i

dizi[1] = (D, 5): açılan boşluğa yerleşir.

0B:51D:52A:23C:145…

boyut++ → 4. Dizi yine büyükten küçüğe sıralı.

public void ekle(E veri, int p) {
  if (boyut == dizi.length)
    dizi = Arrays.copyOf(dizi, 2 * boyut);
  int i = boyut;
  while (i > 0 && dizi[i-1].oncelik < p) {
    dizi[i] = dizi[i - 1];  // sağa kaydır
    i--;
  }
  dizi[i] = new Oge<>(veri, p);
  boyut++;
}

Liste ile Uygulama

Maliyet Karşılaştırması

İşlemSırasız listeSıralı listeİkili heap
size, isEmptyO(1)O(1)O(1)
insertO(1)O(n)O(log n)
minO(n)O(1)O(1)
removeMinO(n)O(1)O(log n)
n öğeyle doldurmaO(n)O(n log n)*O(n)**

* Önce sıralayarak.   ** Aşağıdan yukarıya heap oluşturma ile.

n ekleme + n çıkarma: sırasız ve sıralı listede O(n²), heap'te O(n log n). n = 10⁶ için fark ≈ 50.000 kat.

İ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:

907182335465sonraki ekleme6seviye 0seviye 1seviye 2← kök = en büyük
Kardeşler arasında sıra yok: 7 ile 8 yer değişse de heap olur.
1. Heap-sıra özelliğiHer düğüm çocuklarından önce gelir: max-heap'te ebeveyn ≥ çocuk. En yüksek öncelikli öğe köktedir.
2. Tam ikili ağaç özelliğiSon seviye hariç tüm seviyeler dolu, son seviye soldan sağa dolar → yükseklik ⌊log₂ n⌋, dizide boşluk yok.
Ekleme ve çıkarma yalnızca kökten yaprağa uzanan tek bir yol üzerinde çalışır → O(log n). Heap, ikili arama ağacı değildir.

İkili Heap

Min-Heap ve Max-Heap

0739856
Min-Heap: kökte en küçük anahtar
anahtar(ebeveyn) ≤ anahtar(çocuk)
9362154
Max-Heap: kökte en büyük anahtar
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)

12345
Tam ✓
son seviye soldan dolu
12346
Tam değil ✗
son seviyede boşluk var
12367
Tam değil ✗
son seviye sola yaslı değil
Yükseklikd. seviyede en çok 2d düğüm bulunur. n düğümlü tam ikili ağacın yüksekliği h = ⌊log₂ n⌋.n = 7 → h = 2 · n = 1000 → h = 9
Neden önemli?Yeni öğe her zaman "son seviyede en soldaki boş yere" gelir. Bu konum dizide tam olarak indis n'dir.

İkili Heap

Heap mi, Değil mi?

1089357
(a)
10893127
(b)
13254
(c)
108976
(d)

Her ağaç için karar verin: max-heap mi, min-heap mi, yoksa heap değil mi? Hangi özellik bozuluyor?

Cevapları göster
  1. Max-heap ✓ (tam ağaç, her ebeveyn ≥ çocukları).
  2. Heap değil: 12 > ebeveyni 8 → heap-sıra özelliği bozuk.
  3. Min-heap ✓ (tam ağaç, her ebeveyn ≤ çocukları).
  4. Heap değil: 8'in çocukları yokken 9'un çocukları var → tam ağaç değil.

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

Kod İzleme: heapMi([10, 8, 9, 3, 5, 7, 4, 6])

10081923354754667heapMi(a)n = 801018293345576476

heapMi(a) çağrıldı: 8 öğeli dizi. Kök (indis 0) hariç her düğüm, ebeveyniyle bir kez karşılaştırılacak.

100e81i923354754667heapMi(a)n = 801018293345576476▲ i▲ e

i = 1: ebeveyn e = (1 − 1) / 2 = 0. a[0] = 10 ≥ a[1] = 8 ✓ → devam.

100e8192i3354754667heapMi(a)n = 801018293345576476▲ i▲ e

i = 2: ebeveyn e = (2 − 1) / 2 = 0. a[0] = 10 ≥ a[2] = 9 ✓ → devam.

10081e9233i54754667heapMi(a)n = 801018293345576476▲ i▲ e

i = 3: ebeveyn e = (3 − 1) / 2 = 1. a[1] = 8 ≥ a[3] = 3 ✓ → devam.

10081e923354i754667heapMi(a)n = 801018293345576476▲ i▲ e

i = 4: ebeveyn e = (4 − 1) / 2 = 1. a[1] = 8 ≥ a[4] = 5 ✓ → devam.

1008192e335475i4667heapMi(a)n = 801018293345576476▲ i▲ e

i = 5: ebeveyn e = (5 − 1) / 2 = 2. a[2] = 9 ≥ a[5] = 7 ✓ → devam.

1008192e33547546i67heapMi(a)n = 801018293345576476▲ i▲ e

i = 6: ebeveyn e = (6 − 1) / 2 = 2. a[2] = 9 ≥ a[6] = 4 ✓ → devam.

100819233e54754667iheapMi(a)false01018293345576476▲ i▲ e

i = 7: e = 3. a[3] = 3 < a[7] = 6 ✗ → çocuk ebeveynden büyük: return false.

10081923354754667heapMi(a)false01018293345576476

Sonuç false: ilk yedi öğe geçerli bir heap olsa da 6, ebeveyni 3'ten büyük. En kötü durumda n − 1 karşılaştırma → O(n).

static boolean heapMi(int[] a) {  // max-heap mi?
  for (int i = 1; i < a.length; i++) {
    int e = (i - 1) / 2;           // ebeveyn
    if (a[e] < a[i]) return false; // sıra bozuk
  }
  return true;
}

İkili Heap

Dizi Gösterimi

  • Düğümler kökten başlayarak seviye seviye, 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.
90316223145546heap[]90316223145546

Düğümlerin yanındaki küçük sayılar dizideki indisleridir. Bu sunumda indisler 0'dan başlar; kök heap[0]'dadır.

İkili Heap

Ebeveyn ve Çocuk Hesaplamaları

90316223145546heap[]90316223145546
Çocuklarsol(i) = 2i + 1 · sağ(i) = 2i + 2i = 1 → 3 ve 4 · i = 2 → 5 ve 6
Ebeveynebeveyn(i) = ⌊(i − 1) / 2⌋i = 6 → 2 · i = 5 → 2 · i = 4 → 1
  • Java'da tam sayı bölmesi zaten aşağı yuvarlar: (i - 1) / 2. Çocuk yoksa 2i + 1 ≥ n olur.
  • 1 tabanlı alternatif: 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 (yukarı süzme)

  1. Yeni öğe ağacın ilk boş konumuna (heap[n]) eklenir. Ağaç tam kalır.
  2. Heap-sıra özelliği bozulmuş olabilir: yeni öğe ebeveyninden büyükse (max-heap) yer değiştirilir.
  3. Öğe köke ulaşana ya da ebeveyni kendisinden büyük/eşit olana kadar yukarı taşınır.
12907182335465106heap[6] = 10 eklendi → 2 takasla köke çıkar
MaliyetEn çok h = ⌊log₂ n⌋ yer değiştirme → O(log n).
Neden doğru?Yer değiştirmede aşağı inen ebeveyn, zaten diğer çocuğundan büyüktü.
Adlarıup-heap, bubble-up, sift-up, swim; bu bölümün kodunda yuzdur.

Ekleme ve Silme

Max-Heap'e Ekleme: Java Kodu

public class MaxHeap {
  private int[] heap = new int[4];
  private int n = 0;                // öğe sayısı

public void ekle(int x) {
  if (n == heap.length) buyut(2 * heap.length);
  heap[n] = x;  n++;              // ilk boş konum
  yuzdur(n - 1);
}
private void yuzdur(int i) {      // up-heap
  while (i > 0) {
    int ebeveyn = (i - 1) / 2;
    if (heap[ebeveyn] >= heap[i]) return;
    yerDegistir(i, ebeveyn);  i = ebeveyn;
  }
}
buyutDizi dolunca kapasite iki katına çıkar (amortize O(1)).
yerDegistirheap[a] ↔ heap[b] takası.
DurmaKökte (i = 0) ya da ebeveyn ≥ öğe olunca.

Ekleme ve Silme · Kodu adım adım çalıştır

Kod İzleme: ekle(10), ekle(8)

90316223145546başlangıçn = 7 · heap.length = 160913263241556478

Başlangıç max-heap: her ebeveyn ≥ çocukları, en büyük (9) kökte. Kapasite 16, yer var.

90316223145546107yeniekle(10)n = 8 · heap.length = 16091326324155647108▲ yeni

ekle(10): 10, ilk boş konuma heap[7]'ye yazılır; n = 8. Ağaç tam kalır ama sıra bozulmuş olabilir.

90316223ebeveyn145546107iekle(10)n = 8 · heap.length = 16091326324155647108▲ i▲ ebeveyn

i = 7 > 0 → ebeveyn = (7 − 1) / 2 = 3. Karşılaştır: heap[3] = 2 < heap[7] = 10.

903162103i14554627ekle(10)n = 8 · heap.length = 16091326310415564728▲ i

10 > 2 → yer değiştir (indis 7 ↔ 3); öğe bir seviye yukarı çıktı, i = 3.

9031ebeveyn62103i14554627ekle(10)n = 8 · heap.length = 16091326310415564728▲ i▲ ebeveyn

i = 3 > 0 → ebeveyn = (3 − 1) / 2 = 1. Karşılaştır: heap[1] = 3 < heap[3] = 10.

90101i623314554627ekle(10)n = 8 · heap.length = 16091102633415564728▲ i

10 > 3 → yer değiştir (indis 3 ↔ 1); öğe bir seviye yukarı çıktı, i = 1.

90ebeveyn101i623314554627ekle(10)n = 8 · heap.length = 16091102633415564728▲ i▲ ebeveyn

i = 1 > 0 → ebeveyn = (1 − 1) / 2 = 0. Karşılaştır: heap[0] = 9 < heap[1] = 10.

100i91623314554627ekle(10)n = 8 · heap.length = 16010192633415564728▲ i

10 > 9 → yer değiştir (indis 1 ↔ 0); öğe bir seviye yukarı çıktı, i = 0.

100i91623314554627ekle(10)n = 8 · heap.length = 16010192633415564728▲ i

i = 0: öğe kökte, while koşulu (i > 0) yanlış → yuzdur biter. 10 yerine oturdu.

1009162331455462788yeniekle(8)n = 9 · heap.length = 160101926334155647288▲ yeni

ekle(8): 8, ilk boş konuma heap[8]'e yazılır; n = 9. Ağaç tam kalır ama sıra bozulmuş olabilir.

100916233ebeveyn1455462788iekle(8)n = 9 · heap.length = 160101926334155647288▲ i▲ ebeveyn

i = 8 > 0 → ebeveyn = (8 − 1) / 2 = 3. Karşılaştır: heap[3] = 3 < heap[8] = 8.

100916283i1455462738ekle(8)n = 9 · heap.length = 160101926384155647283▲ i

8 > 3 → yer değiştir (indis 8 ↔ 3); öğe bir seviye yukarı çıktı, i = 3.

10091ebeveyn6283i1455462738ekle(8)n = 9 · heap.length = 160101926384155647283▲ i▲ ebeveyn

i = 3 > 0 → ebeveyn = (3 − 1) / 2 = 1. Karşılaştır: heap[1] = 9 ≥ heap[3] = 8.

10091ebeveyn6283i1455462738ekle(8)n = 9 · heap.length = 160101926384155647283▲ i▲ ebeveyn

9 ≥ 8: ebeveyn zaten büyük/eşit → heap-sıra sağlandı, return. 8 yerine oturdu.

public void ekle(int x) {
  if (n == heap.length) buyut(2 * heap.length);
  heap[n] = x;  n++;              // ilk boş konum
  yuzdur(n - 1);
}
private void yuzdur(int i) {      // up-heap
  while (i > 0) {
    int ebeveyn = (i - 1) / 2;
    if (heap[ebeveyn] >= heap[i]) return;
    yerDegistir(i, ebeveyn);  i = ebeveyn;
  }
}

Ekleme ve Silme · Kodu adım adım çalıştır

Kod İzleme: boş heap'e 4, 5, 2, 6 ekle

40yeniekle(4)n = 1 · heap.length = 40412345▲ yeni

ekle(4): 4, ilk boş konuma heap[0]'a yazılır; n = 1. Ağaç tam kalır ama sıra bozulmuş olabilir.

40iekle(4)n = 1 · heap.length = 40412345▲ i

i = 0: öğe kökte, while koşulu (i > 0) yanlış → yuzdur biter. 4 yerine oturdu.

4051yeniekle(5)n = 2 · heap.length = 404152345▲ yeni

ekle(5): 5, ilk boş konuma heap[1]'e yazılır; n = 2. Ağaç tam kalır ama sıra bozulmuş olabilir.

40ebeveyn51iekle(5)n = 2 · heap.length = 404152345▲ i▲ ebeveyn

i = 1 > 0 → ebeveyn = (1 − 1) / 2 = 0. Karşılaştır: heap[0] = 4 < heap[1] = 5.

50i41ekle(5)n = 2 · heap.length = 405142345▲ i

5 > 4 → yer değiştir (indis 1 ↔ 0); öğe bir seviye yukarı çıktı, i = 0.

50i41ekle(5)n = 2 · heap.length = 405142345▲ i

i = 0: öğe kökte, while koşulu (i > 0) yanlış → yuzdur biter. 5 yerine oturdu.

504122yeniekle(2)n = 3 · heap.length = 4051422345▲ yeni

ekle(2): 2, ilk boş konuma heap[2]'ye yazılır; n = 3. Ağaç tam kalır ama sıra bozulmuş olabilir.

50ebeveyn4122iekle(2)n = 3 · heap.length = 4051422345▲ i▲ ebeveyn

i = 2 > 0 → ebeveyn = (2 − 1) / 2 = 0. Karşılaştır: heap[0] = 5 ≥ heap[2] = 2.

50ebeveyn4122iekle(2)n = 3 · heap.length = 4051422345▲ i▲ ebeveyn

5 ≥ 2: ebeveyn zaten büyük/eşit → heap-sıra sağlandı, return. 2 yerine oturdu.

50412263yeniekle(6)n = 4 · heap.length = 40514223645▲ yeni

ekle(6): 6, ilk boş konuma heap[3]'e yazılır; n = 4. Ağaç tam kalır ama sıra bozulmuş olabilir.

5041ebeveyn2263iekle(6)n = 4 · heap.length = 40514223645▲ i▲ ebeveyn

i = 3 > 0 → ebeveyn = (3 − 1) / 2 = 1. Karşılaştır: heap[1] = 4 < heap[3] = 6.

5061i2243ekle(6)n = 4 · heap.length = 40516223445▲ i

6 > 4 → yer değiştir (indis 3 ↔ 1); öğe bir seviye yukarı çıktı, i = 1.

50ebeveyn61i2243ekle(6)n = 4 · heap.length = 40516223445▲ i▲ ebeveyn

i = 1 > 0 → ebeveyn = (1 − 1) / 2 = 0. Karşılaştır: heap[0] = 5 < heap[1] = 6.

60i512243ekle(6)n = 4 · heap.length = 40615223445▲ i

6 > 5 → yer değiştir (indis 1 ↔ 0); öğe bir seviye yukarı çıktı, i = 0.

60i512243ekle(6)n = 4 · heap.length = 40615223445▲ i

i = 0: öğe kökte, while koşulu (i > 0) yanlış → yuzdur biter. 6 yerine oturdu.

public void ekle(int x) {
  if (n == heap.length) buyut(2 * heap.length);
  heap[n] = x;  n++;              // ilk boş konum
  yuzdur(n - 1);
}
private void yuzdur(int i) {      // up-heap
  while (i > 0) {
    int ebeveyn = (i - 1) / 2;
    if (heap[ebeveyn] >= heap[i]) return;
    yerDegistir(i, ebeveyn);  i = ebeveyn;
  }
}

Ekleme ve Silme · Kodu adım adım çalıştır

Kod İzleme (devam): ekle(1), ekle(3)

60512243durumn = 4 · heap.length = 40615223445

Başlangıç: heap = [6, 5, 2, 4], n = 4 ve kapasite 4 → dizi tamamen dolu.

60512243ekle(1)n = 4 · heap.length = 80615223445

ekle(1): n == heap.length (4) → dizi dolu. buyut(8): kapasite iki katına çıkar, öğeler yeni diziye kopyalanır.

6051224314yeniekle(1)n = 5 · heap.length = 806152234415▲ yeni

1, ilk boş konuma heap[4]'e yazılır; n = 5. Ağaç tam kalır ama sıra bozulmuş olabilir.

6051ebeveyn224314iekle(1)n = 5 · heap.length = 806152234415▲ i▲ ebeveyn

i = 4 > 0 → ebeveyn = (4 − 1) / 2 = 1. Karşılaştır: heap[1] = 5 ≥ heap[4] = 1.

6051ebeveyn224314iekle(1)n = 5 · heap.length = 806152234415▲ i▲ ebeveyn

5 ≥ 1: ebeveyn zaten büyük/eşit → heap-sıra sağlandı, return. 1 yerine oturdu.

605122431435yeniekle(3)n = 6 · heap.length = 8061522344153▲ yeni

ekle(3): 3, ilk boş konuma heap[5]'e yazılır; n = 6. Ağaç tam kalır ama sıra bozulmuş olabilir.

605122ebeveyn431435iekle(3)n = 6 · heap.length = 8061522344153▲ i▲ ebeveyn

i = 5 > 0 → ebeveyn = (5 − 1) / 2 = 2. Karşılaştır: heap[2] = 2 < heap[5] = 3.

605132i431425ekle(3)n = 6 · heap.length = 8061523344152▲ i

3 > 2 → yer değiştir (indis 5 ↔ 2); öğe bir seviye yukarı çıktı, i = 2.

60ebeveyn5132i431425ekle(3)n = 6 · heap.length = 8061523344152▲ i▲ ebeveyn

i = 2 > 0 → ebeveyn = (2 − 1) / 2 = 0. Karşılaştır: heap[0] = 6 ≥ heap[2] = 3.

60ebeveyn5132i431425ekle(3)n = 6 · heap.length = 8061523344152▲ i▲ ebeveyn

6 ≥ 3: ebeveyn zaten büyük/eşit → heap-sıra sağlandı, return. 3 yerine oturdu.

public void ekle(int x) {
  if (n == heap.length) buyut(2 * heap.length);
  heap[n] = x;  n++;              // ilk boş konum
  yuzdur(n - 1);
}
private void yuzdur(int i) {      // up-heap
  while (i > 0) {
    int ebeveyn = (i - 1) / 2;
    if (heap[ebeveyn] >= heap[i]) return;
    yerDegistir(i, ebeveyn);  i = ebeveyn;
  }
}

Ekleme ve Silme · Kodu adım adım çalıştır

Kod İzleme: buyut(8)

60512243buyut(8)heap6524

ekle(1) çağrıldı; n == heap.length = 4 → buyut(8). Dizinin boyu sabittir; büyütmek = yeni dizi + kopyalama.

60512243buyut(8)heap6524yeni0001020304050607

8 hücrelik yeni dizi ayrılır; Java int dizisini 0 ile doldurur.

60512243buyut(8)heap6524yeni6001020304050607

k = 0 < n → yeni[0] = heap[0] = 6.

60512243buyut(8)heap6524yeni6051020304050607

k = 1 < n → yeni[1] = heap[1] = 5.

60512243buyut(8)heap6524yeni6051220304050607

k = 2 < n → yeni[2] = heap[2] = 2.

60512243buyut(8)heap6524yeni6051224304050607

k = 3 < n → yeni[3] = heap[3] = 4.

60512243buyut(8)heap6524yeni6051224304050607

k = 4 = n → döngü biter. n öğe kopyalandı: bu büyütmenin maliyeti O(n).

60512243buyut(8)eski6524heap6051224304050607

heap = yeni: alan artık yeni diziyi gösterir; eski dizi çöp toplayıcıya kalır. Ağaç aynı, yalnızca kapasite 8.

60512243buyut(8)eski6524heap6051224304050607

buyut döner, ekle(1) devam eder. Kapasite her seferinde iki katına çıktığı için n eklemede toplam kopyalama < 2n → ekleme başına amortize O(1).

private void buyut(int kapasite) {
  int[] yeni = new int[kapasite];
  for (int k = 0; k < n; k++)
    yeni[k] = heap[k];          // kopyala
  heap = yeni;                  // eski dizi çöpe
}

Ekleme ve Silme

En Büyüğü Çıkarma: Down-Heap (aşağı süzme)

  1. Kökteki öğe (en büyük) saklanır; döndürülecek değer budur.
  2. Ağacın son öğesi (heap[n − 1]) köke taşınır, n bir azalır.
  3. Kök büyük olan çocukla yer değiştirir.
  4. Öğe yaprağa ulaşana ya da iki çocuğundan da büyük/eşit olana kadar aşağı iner.
124071823354659 çıkarıldı, son öğe 4 köke → büyük çocukla inermax = 9
Neden büyük çocuk?Küçük çocuk yukarı çıksaydı, diğer (büyük) çocuktan küçük bir ebeveyn olurdu. Min-heap'te ise küçük çocuk seçilir.
MaliyetHer seviyede 2 karşılaştırma, en çok h seviye → O(log n).

Ekleme ve Silme

En Büyüğü Çıkarma: Java Kodu

public int silMax() {           // n > 0 olmalı
  int max = heap[0];
  yerDegistir(0, n - 1);  n--;  // son öğe köke
  batir(0);
  return max;
}
private void batir(int i) {       // down-heap
  while (2 * i + 1 < n) {       // sol çocuk var mı?
    int j = 2 * i + 1;            // j: büyük çocuk
    if (j + 1 < n && heap[j + 1] > heap[j]) j++;
    if (heap[i] >= heap[j]) return;
    yerDegistir(i, j);  i = j;    // bir seviye in
  }
}
Boş heapGerçek kodda önce if (n == 0) ile istisna fırlatılır.
kucultİsteğe bağlı: n = ¼ kapasite olunca dizi yarıya indirilir.
TakasMax dizinin sonunda kalır → heap sort bunu kullanır.

Ekleme ve Silme · Kodu adım adım çalıştır

Kod İzleme: silMax() ve batir

90max316223145546silMax()n = 709132632415564▲ max

silMax(): en büyük öğe her zaman kökte → max = heap[0] = 9.

40316223145596silMax()n = 604132632415569

Kök ile son öğe yer değiştirir (0 ↔ 6), n = 6: 9 artık heap dışında (soluk). Ağaç tam kaldı; yeni kök 4 sırayı bozabilir.

40i31sol62sağ,j23145596silMax()n = 604132632415569▲ i▲ sol▲ sağj

Sol çocuk 1 (3), sağ çocuk 2 (6) → büyük olan 6, j = 2.

603142i23145596silMax()n = 606132432415569▲ i

4 < 6 → yer değiştir (indis 0 ↔ 2); büyük çocuk yukarı çıktı, i = 2.

603142i231455sol,j96silMax()n = 606132432415569▲ i▲ solj

Yalnızca sol çocuk var: indis 5 (5) → j = 5.

603152231445i96silMax()n = 606132532415469▲ i

4 < 5 → yer değiştir (indis 2 ↔ 5); büyük çocuk yukarı çıktı, i = 5.

603152231445i96silMax()n = 606132532415469▲ i

i = 5: 2·5 + 1 = 11 ≥ n = 6 → çocuk yok (yaprak), döngü biter.

60315223144596silMax()n = 606132532415469

batir bitti; yine geçerli bir max-heap. return 9. Yol boyu en çok ⌊log₂ n⌋ seviye → O(log n).

public int silMax() {           // n > 0 olmalı
  int max = heap[0];
  yerDegistir(0, n - 1);  n--;  // son öğe köke
  batir(0);
  return max;
}
private void batir(int i) {       // down-heap
  while (2 * i + 1 < n) {       // sol çocuk var mı?
    int j = 2 * i + 1;            // j: büyük çocuk
    if (j + 1 < n && heap[j + 1] > heap[j]) j++;
    if (heap[i] >= heap[j]) return;
    yerDegistir(i, j);  i = j;    // bir seviye in
  }
}

Ekleme ve Silme · Kodu adım adım çalıştır

Kod İzleme: art arda iki silMax()

100max9162831455462738silMax() #1n = 90101926384155647283▲ max

silMax() #1: en büyük öğe her zaman kökte → max = heap[0] = 10.

3091628314554627108silMax() #1n = 80319263841556472810

Kök ile son öğe yer değiştirir (0 ↔ 8), n = 8: 10 artık heap dışında (soluk). Ağaç tam kaldı; yeni kök 3 sırayı bozabilir.

30i91sol,j62sağ8314554627108silMax() #1n = 80319263841556472810▲ i▲ solj▲ sağ

Sol çocuk 1 (9), sağ çocuk 2 (6) → büyük olan 9, j = 1.

9031i628314554627108silMax() #1n = 80913263841556472810▲ i

3 < 9 → yer değiştir (indis 0 ↔ 1); büyük çocuk yukarı çıktı, i = 1.

9031i6283sol,j14sağ554627108silMax() #1n = 80913263841556472810▲ i▲ solj▲ sağ

Sol çocuk 3 (8), sağ çocuk 4 (1) → büyük olan 8, j = 3.

90816233i14554627108silMax() #1n = 80918263341556472810▲ i

3 < 8 → yer değiştir (indis 1 ↔ 3); büyük çocuk yukarı çıktı, i = 3.

90816233i14554627sol,j108silMax() #1n = 80918263341556472810▲ i▲ solj

Yalnızca sol çocuk var: indis 7 (2) → j = 7.

90816233i14554627j108silMax() #1n = 80918263341556472810▲ i▲ j

heap[3] = 3 ≥ heap[7] = 2 → iki çocuktan da büyük/eşit, sıra sağlandı: return.

9081623314554627108silMax() #1n = 80918263341556472810

batir bitti; yine geçerli bir max-heap. return 10. Yol boyu en çok ⌊log₂ n⌋ seviye → O(log n).

90max81623314554627108silMax() #2n = 80918263341556472810▲ max

silMax() #2: en büyük öğe her zaman kökte → max = heap[0] = 9.

2081623314554697108silMax() #2n = 70218263341556479810

Kök ile son öğe yer değiştirir (0 ↔ 7), n = 7: 9 artık heap dışında (soluk). Ağaç tam kaldı; yeni kök 2 sırayı bozabilir.

20i81sol,j62sağ3314554697108silMax() #2n = 70218263341556479810▲ i▲ solj▲ sağ

Sol çocuk 1 (8), sağ çocuk 2 (6) → büyük olan 8, j = 1.

8021i623314554697108silMax() #2n = 70812263341556479810▲ i

2 < 8 → yer değiştir (indis 0 ↔ 1); büyük çocuk yukarı çıktı, i = 1.

8021i6233sol,j14sağ554697108silMax() #2n = 70812263341556479810▲ i▲ solj▲ sağ

Sol çocuk 3 (3), sağ çocuk 4 (1) → büyük olan 3, j = 3.

80316223i14554697108silMax() #2n = 70813263241556479810▲ i

2 < 3 → yer değiştir (indis 1 ↔ 3); büyük çocuk yukarı çıktı, i = 3.

80316223i14554697108silMax() #2n = 70813263241556479810▲ i

i = 3: 2·3 + 1 = 7 ≥ n = 7 → çocuk yok (yaprak), döngü biter.

8031622314554697108silMax() #2n = 70813263241556479810

batir bitti; yine geçerli bir max-heap. return 9. Yol boyu en çok ⌊log₂ n⌋ seviye → O(log n).

public int silMax() {           // n > 0 olmalı
  int max = heap[0];
  yerDegistir(0, n - 1);  n--;  // son öğe köke
  batir(0);
  return max;
}
private void batir(int i) {       // down-heap
  while (2 * i + 1 < n) {       // sol çocuk var mı?
    int j = 2 * i + 1;            // j: büyük çocuk
    if (j + 1 < n && heap[j + 1] > heap[j]) j++;
    if (heap[i] >= heap[j]) return;
    yerDegistir(i, j);  i = j;    // bir seviye in
  }
}

Ekleme ve Silme

Min-Heap: insert(1) ve removeMin()

20513293648546heap[]20513293648546min-heapn = 7

Başlangıç min-heap: her ebeveyn ≤ çocukları; en küçük öğe (2) kökte.

2051329364854617heap[]2051329364854617insert(1)n = 8

insert(1): 1, ilk boş konuma (indis 7) eklenir. Ebeveyni: (7−1)/2 = 3 → 9.

2051321364854697heap[]2051321364854697insert(1)n = 8

1 < 9 → yer değiştir (indis 7 ↔ 3).

2011325364854697heap[]2011325364854697insert(1)n = 8

1 < 5 → yer değiştir (indis 3 ↔ 1).

1021325364854697heap[]1021325364854697insert(1)n = 8

1 < 2 → yer değiştir (indis 1 ↔ 0). Kök → dur.

1021325364854697heap[]1021325364854697removeMin()n = 8

removeMin(): min = heap[0] = 1 (kök); döndürülecek değer.

9021325364854617heap[]9021325364854617removeMin()n = 7

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.

2091325364854617heap[]2091325364854617removeMin()n = 7

Çocuklar: 2 ve 3 → küçük olan 2. 9 > 2 → yer değiştir (0 ↔ 1).

2051329364854617heap[]2051329364854617removeMin()n = 7

Çocuklar: 5 ve 6 → küçük olan 5. 9 > 5 → yer değiştir (1 ↔ 3). İndis 3 yaprak → dur.

Ekleme ve Silme

Heap İşlemlerinin Maliyeti

İşlemMaliyetNeden?
size, isEmptyO(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 aramaO(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.

Ekleme ve Silme

Uyarlanabilir Öncelikli Kuyruk (Adaptable PQ)

Bazen kuyruktaki bir öğenin önceliği değişir (Dijkstra'da mesafe kısalır, acil serviste hasta ağırlaşır) ya da öğe sıradan çıkarılır. Goodrich bu ek işlemleri tanımlar:

90316223145546Entry: anahtar = 1indis = 4elimizdekireferans eheap[]
Konum (locator): öğe, heap dizisindeki yerini bilir; her takasta güncellenir.
replaceKey(e, k)heap[e.indis] değişir; büyüdüyse up-heap, küçüldüyse down-heap (max-heap).
remove(e)Son öğeyle takas, n − 1; yerine gelen öğe için up- ya da down-heap.
Neden konum?Konumsuz öğeyi bulmak O(n); konumla O(1) → işlemler O(log n).

Ekleme ve Silme · Kodu adım adım çalıştır

Kod İzleme: anahtarDegistir(4, 8)

9031622314i5546anahtarDegistir(4, 8)n = 709132632415564▲ i

anahtarDegistir(4, 8): indis 4'teki anahtar 1. Eski değeri sakla: eski = 1.

9031622384i5546anahtarDegistir(4, 8)n = 709132632485564▲ i

heap[4] = 8. Ağaç hâlâ tam, ama heap-sıra özelliği bu düğümün çevresinde bozulmuş olabilir.

9031622384i5546anahtarDegistir(4, 8)n = 709132632485564▲ i

8 > 1: anahtar büyüdü → yalnızca ebeveynle çatışabilir, yuzdur(4) çağrılır.

9031ebeveyn622384i5546yuzdurn = 709132632485564▲ i▲ ebeveyn

Ebeveyn (4 − 1) / 2 = 1: heap[1] = 3 < 8 → yer değiştirmeli.

9081i6223345546yuzdurn = 709182632435564▲ i

Takas (indis 4 ↔ 1); i = 1. Aşağı inen 3, zaten diğer çocuğundan büyüktü.

90ebeveyn81i6223345546yuzdurn = 709182632435564▲ i▲ ebeveyn

Ebeveyn 0: heap[0] = 9 ≥ 8 → sıra sağlandı, return.

90816223345546anahtarDegistir(4, 8)n = 709182632435564

anahtarDegistir bitti: 8 bir seviye yukarı çıktı. Yalnızca 4 → 1 → 0 yolu incelendi: O(log n).

public void anahtarDegistir(int i, int yeni) {
  int eski = heap[i];
  heap[i] = yeni;
  if (yeni > eski) yuzdur(i);  // büyüdü: yukarı
  else batir(i);               // küçüldü: aşağı
}
private void yuzdur(int i) {     // up-heap
  while (i > 0) {
    int ebeveyn = (i - 1) / 2;
    if (heap[ebeveyn] >= heap[i]) return;
    yerDegistir(i, ebeveyn);  i = ebeveyn;
  }
}

Ekleme ve Silme · Kodu adım adım çalıştır

Kod İzleme (devam): anahtarDegistir(0, 1)

90i816223345546anahtarDegistir(0, 1)n = 709182632435564▲ i

anahtarDegistir(0, 1): indis 0'daki anahtar 9. Eski değeri sakla: eski = 9.

10i816223345546anahtarDegistir(0, 1)n = 701182632435564▲ i

heap[0] = 1. Ağaç hâlâ tam, ama heap-sıra özelliği bu düğümün çevresinde bozulmuş olabilir.

10i816223345546anahtarDegistir(0, 1)n = 701182632435564▲ i

1 < 9: anahtar küçüldü → yalnızca çocuklarla çatışabilir, batir(0) çağrılır.

10i81sol,j62sağ23345546batirn = 701182632435564▲ i▲ solj▲ sağ

Çocuklar 8 ve 6 → büyük olan 8, j = 1.

8011i6223345546batirn = 708112632435564▲ i

1 < 8 → takas (0 ↔ 1), büyük çocuk yukarı çıktı; i = 1.

8011i6223sol34sağ,j5546batirn = 708112632435564▲ i▲ sol▲ sağj

Çocuklar 2 ve 3 → büyük olan 3, j = 4.

8031622314i5546batirn = 708132632415564▲ i

1 < 3 → takas (1 ↔ 4), büyük çocuk yukarı çıktı; i = 4.

8031622314i5546batirn = 708132632415564▲ i

i = 4: 2·4 + 1 = 9 ≥ n → yaprak, döngü biter.

80316223145546anahtarDegistir(0, 1)n = 708132632415564

Bitti: 1 iki seviye indi, 8 köke çıktı. Kök için anahtarDegistir(0, x) + küçük x, silMax'ın batırma adımıyla aynı işi yapar.

public void anahtarDegistir(int i, int yeni) {
  int eski = heap[i];
  heap[i] = yeni;
  if (yeni > eski) yuzdur(i);  // büyüdü: yukarı
  else batir(i);               // küçüldü: aşağı
}
private void batir(int i) {       // down-heap
  while (2 * i + 1 < n) {
    int j = 2 * i + 1;              // j: büyük çocuk
    if (j + 1 < n && heap[j + 1] > heap[j]) j++;
    if (heap[i] >= heap[j]) return;
    yerDegistir(i, j);  i = j;
  }
}

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 k = n / 2 - 1; k >= 0; k--)   // son iç düğümden köke
    batir(k);                            // alt ağaçları heap olan k'yi batır

Heap Oluşturma ve Heap Sort · Kodu adım adım çalıştır

Kod İzleme: heapOlustur() (k = 4, 3, 2)

40113223164k951061478879heapOlustur()n = 1004112332416596107148897▲ k

Sıradan bir dizi (heap değil). İndis 5…9 yapraktır, tek başına zaten heap. k = n/2 − 1 = 4 (son iç düğüm) ile başlanır.

40113223164i951061478879sol,jbatir(4)n = 1004112332416596107148897▲ i▲ solj

k = 4 → batir(4). Yalnızca sol çocuk var: indis 9 (7) → j = 9.

40113223164i951061478879jbatir(4)n = 1004112332416596107148897▲ i▲ j

heap[4] = 16 ≥ heap[9] = 7 → iki çocuktan da büyük/eşit, sıra sağlandı: return.

40113223i16495106147sol,j88sağ79batir(3)n = 1004112332416596107148897▲ i▲ solj▲ sağ

k = 3 → batir(3). Sol çocuk 7 (14), sağ çocuk 8 (8) → büyük olan 14, j = 7.

4011321431649510627i8879batir(3)n = 1004112331441659610728897▲ i

2 < 14 → yer değiştir (indis 3 ↔ 7); büyük çocuk yukarı çıktı, i = 7.

4011321431649510627i8879batir(3)n = 1004112331441659610728897▲ i

i = 7: 2·7 + 1 = 15 ≥ n = 10 → çocuk yok (yaprak), döngü biter.

401132i14316495sol106sağ,j278879batir(2)n = 1004112331441659610728897▲ i▲ sol▲ sağj

k = 2 → batir(2). Sol çocuk 5 (9), sağ çocuk 6 (10) → büyük olan 10, j = 6.

40111021431649536i278879batir(2)n = 1004112103144165963728897▲ i

3 < 10 → yer değiştir (indis 2 ↔ 6); büyük çocuk yukarı çıktı, i = 6.

40111021431649536i278879batir(2)n = 1004112103144165963728897▲ i

i = 6: 2·6 + 1 = 13 ≥ n = 10 → çocuk yok (yaprak), döngü biter.

public void heapOlustur() {     // rastgele dizi
  for (int k = n / 2 - 1; k >= 0; k--)
    batir(k);                   // alt ağaçlar heap
}
private void batir(int i) {       // down-heap
  while (2 * i + 1 < n) {       // sol çocuk var mı?
    int j = 2 * i + 1;            // j: büyük çocuk
    if (j + 1 < n && heap[j + 1] > heap[j]) j++;
    if (heap[i] >= heap[j]) return;
    yerDegistir(i, j);  i = j;    // bir seviye in
  }
}

Heap Oluşturma ve Heap Sort · Kodu adım adım çalıştır

Kod İzleme (devam): k = 1, 0

4011i102143sol164sağ,j9536278879batir(1)n = 1004112103144165963728897▲ i▲ sol▲ sağj

k = 1 → batir(1). Sol çocuk 3 (14), sağ çocuk 4 (16) → büyük olan 16, j = 4.

4016110214314i9536278879batir(1)n = 1004116210314415963728897▲ i

1 < 16 → yer değiştir (indis 1 ↔ 4); büyük çocuk yukarı çıktı, i = 4.

4016110214314i9536278879sol,jbatir(1)n = 1004116210314415963728897▲ i▲ solj

Yalnızca sol çocuk var: indis 9 (7) → j = 9.

40161102143749536278819ibatir(1)n = 1004116210314475963728891▲ i

1 < 7 → yer değiştir (indis 4 ↔ 9); büyük çocuk yukarı çıktı, i = 9.

40161102143749536278819ibatir(1)n = 1004116210314475963728891▲ i

i = 9: 2·9 + 1 = 19 ≥ n = 10 → çocuk yok (yaprak), döngü biter.

40i161sol,j102sağ143749536278819batir(0)n = 1004116210314475963728891▲ i▲ solj▲ sağ

k = 0 → batir(0). Sol çocuk 1 (16), sağ çocuk 2 (10) → büyük olan 16, j = 1.

16041i102143749536278819batir(0)n = 1001614210314475963728891▲ i

4 < 16 → yer değiştir (indis 0 ↔ 1); büyük çocuk yukarı çıktı, i = 1.

16041i102143sol,j74sağ9536278819batir(0)n = 1001614210314475963728891▲ i▲ solj▲ sağ

Sol çocuk 3 (14), sağ çocuk 4 (7) → büyük olan 14, j = 3.

16014110243i749536278819batir(0)n = 1001611421034475963728891▲ i

4 < 14 → yer değiştir (indis 1 ↔ 3); büyük çocuk yukarı çıktı, i = 3.

16014110243i74953627sol88sağ,j19batir(0)n = 1001611421034475963728891▲ i▲ sol▲ sağj

Sol çocuk 7 (2), sağ çocuk 8 (8) → büyük olan 8, j = 8.

160141102837495362748i19batir(0)n = 1001611421038475963728491▲ i

4 < 8 → yer değiştir (indis 3 ↔ 8); büyük çocuk yukarı çıktı, i = 8.

160141102837495362748i19batir(0)n = 1001611421038475963728491▲ i

i = 8: 2·8 + 1 = 17 ≥ n = 10 → çocuk yok (yaprak), döngü biter.

16014110283749536274819heapOlustur()n = 1001611421038475963728491

k = −1 → döngü biter. Dizi artık max-heap: her ebeveyn ≥ çocukları, en büyük (16) kökte. Toplam iş O(n).

public void heapOlustur() {     // rastgele dizi
  for (int k = n / 2 - 1; k >= 0; k--)
    batir(k);                   // alt ağaçlar heap
}
private void batir(int i) {       // down-heap
  while (2 * i + 1 < n) {       // sol çocuk var mı?
    int j = 2 * i + 1;            // j: büyük çocuk
    if (j + 1 < n && heap[j + 1] > heap[j]) j++;
    if (heap[i] >= heap[j]) return;
    yerDegistir(i, j);  i = j;    // bir seviye in
  }
}

Heap Oluşturma ve Heap Sort

Aşağıdan Yukarıya Oluşturma Neden O(n)?

Düğümün yüksekliğiDüğümEn çok takasToplam
0 (yapraklar)800
1414
2224
3 (kök)133
n = 1511
  • 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.
Down-heap'in maliyeti düğümün yüksekliğine, up-heap'in maliyeti derinliğine bağlıdır. Düğümlerin çoğu aşağıdadır.

Heap Oluşturma ve Heap Sort

Heap Sort

Aşama 1: heap oluşturDizi aşağıdan yukarıya max-heap'e çevrilir. O(n)
Aşama 2: n − 1 kez silMaxKök (en büyük) dizinin sonundaki öğeyle takas edilir, heap bir küçülür, kök için down-heap. O(n log n)
402132135645max-heap (boyut 4)sıralı kısım← kök buraya
  • 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: Java Kodu

public static void heapSort(int[] a) {
  int n = a.length;
  // 1) oluştur: son iç düğümden köke
  for (int k = n / 2 - 1; k >= 0; k--)
    batir(a, k, n);
  // 2) sırala: en büyüğü sona al
  for (int son = n - 1; son > 0; son--) {
    yerDegistir(a, 0, son);
    batir(a, 0, son);           // heap boyutu = son
  }
}
static void batir(int[] a, int i, int n) {
  while (2 * i + 1 < n) {
    int j = 2 * i + 1;
    if (j + 1 < n && a[j + 1] > a[j]) j++;
    if (a[i] >= a[j]) return;
    yerDegistir(a, i, j);  i = j;
  }
}
Aynı batirMaxHeap sınıfındakiyle aynı; yalnızca dizi ve heap boyutu parametre olarak geçer.
Deneme{4, 1, 3, 2, 16, 9, 10, 14, 8, 7}→ {1, 2, 3, 4, 7, 8, 9, 10, 14, 16}

Heap Oluşturma ve Heap Sort · Kodu adım adım çalıştır

Kod İzleme: heapSort([6, 5, 3, 4, 1, 2])

605132431425heapSort(a)heap boyutu = 6061523344152

Girdi a = [6, 5, 3, 4, 1, 2]. Tüm dizi heap bölgesi; sağda sıralı kısım henüz boş.

6051324314251) oluşturheap boyutu = 6061523344152

Aşama 1: k = 2, 1, 0 için batir çağrılır. Bu dizi zaten max-heap olduğundan her çağrı ilk karşılaştırmada döner; takas yok.

205132431465son = 5heap boyutu = 5021523344156soluk: sıralı kısım

son = 5: kök (en büyük, 6) ile a[5] = 2 yer değiştirir → 6 kesin yerine oturdu, heap boyutu 5.

5021i32431465batir(a, 0, 5)heap boyutu = 5051223344156▲ isoluk: sıralı kısım

batir(a, 0, 5): i = 0 için büyük çocuk a[1] = 5 > 2 → yer değiştir (0 ↔ 1).

50413223i1465batir(a, 0, 5)heap boyutu = 5051423324156▲ isoluk: sıralı kısım

batir(a, 0, 5): i = 1 için büyük çocuk a[3] = 4 > 2 → yer değiştir (1 ↔ 3). İndis 3 yaprak → batir biter.

104132235465son = 4heap boyutu = 4011423324556soluk: sıralı kısım

son = 4: kök (en büyük, 5) ile a[4] = 1 yer değiştirir → 5 kesin yerine oturdu, heap boyutu 4.

4011i32235465batir(a, 0, 4)heap boyutu = 4041123324556▲ isoluk: sıralı kısım

batir(a, 0, 4): i = 0 için büyük çocuk a[1] = 4 > 1 → yer değiştir (0 ↔ 1).

40213213i5465batir(a, 0, 4)heap boyutu = 4041223314556▲ isoluk: sıralı kısım

batir(a, 0, 4): i = 1 için büyük çocuk a[3] = 2 > 1 → yer değiştir (1 ↔ 3). İndis 3 yaprak → batir biter.

102132435465son = 3heap boyutu = 3011223344556soluk: sıralı kısım

son = 3: kök (en büyük, 4) ile a[3] = 1 yer değiştirir → 4 kesin yerine oturdu, heap boyutu 3.

302112i435465batir(a, 0, 3)heap boyutu = 3031221344556▲ isoluk: sıralı kısım

batir(a, 0, 3): i = 0 için büyük çocuk a[2] = 3 > 1 → yer değiştir (0 ↔ 2). İndis 2 yaprak → batir biter.

102132435465son = 2heap boyutu = 2011223344556soluk: sıralı kısım

son = 2: kök (en büyük, 3) ile a[2] = 1 yer değiştirir → 3 kesin yerine oturdu, heap boyutu 2.

2011i32435465batir(a, 0, 2)heap boyutu = 2021123344556▲ isoluk: sıralı kısım

batir(a, 0, 2): i = 0 için büyük çocuk a[1] = 2 > 1 → yer değiştir (0 ↔ 1). İndis 1 yaprak → batir biter.

102132435465son = 1heap boyutu = 1011223344556soluk: sıralı kısım

son = 1: kök (en büyük, 2) ile a[1] = 1 yer değiştirir → 2 kesin yerine oturdu, heap boyutu 1.

102132435465batir(a, 0, 1)heap boyutu = 1011223344556soluk: sıralı kısım

batir(a, 0, 1): heap boyutu 1, kökün çocuğu yok → hiçbir şey yapılmaz.

102132435465sıralıheap boyutu = 0011223344556

son = 0 → döngü biter. Dizi artan sırada: [1, 2, 3, 4, 5, 6]. Ek dizi kullanılmadı (yerinde), toplam O(n log n).

public static void heapSort(int[] a) {
  int n = a.length;
  // 1) oluştur: son iç düğümden köke
  for (int k = n / 2 - 1; k >= 0; k--)
    batir(a, k, n);
  // 2) sırala: en büyüğü sona al
  for (int son = n - 1; son > 0; son--) {
    yerDegistir(a, 0, son);
    batir(a, 0, son);           // heap boyutu = son
  }
}

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:

Uygulaman × insertn × removeMinOrtaya çıkan algoritma
Sırasız listeO(n)O(n²)Seçimli sıralama (selection sort), O(n²)
Sıralı listeO(n²)O(n)Eklemeli sıralama (insertion sort), O(n²)
İkili heapO(n log n)O(n log n)Heap sort, O(n log n)
Aynı ADT, farklı veri yapısı: çalışma süresi O(n²)'den O(n log n)'e iner. ADT ile veri yapısı ayrımının somut bir örneği.

Heap Oluşturma ve Heap Sort

Heap Türleri: d'li, Binom, Fibonacci

97845236113'lü max-heap: çocuklar 3i+1, 3i+2, 3i+3
  • d'li heap: her düğümün d çocuğu; ebeveyn (i − 1) / d. Yükseklik logd n → ekleme hızlanır, silmede her seviyede d çocuk karşılaştırılır.
  • Binom / Fibonacci heap: ağaç ormanı; iki kuyruğu birleştirme ve anahtar küçültme hızlıdır (Dijkstra, Prim).
Min-heap türüinsertremoveMinanahtar küçültbirleştir
İkili heapO(log n)O(log n)O(log n)O(n)
d'li heapO(logd n)O(d logd n)O(logd n)O(n)
Binom heapO(log n)O(log n)O(log n)O(log n)
Fibonacci heapO(1)O(log n)*O(1)*O(1)

* amortize. Uygulamada ikili heap, basitliği ve önbellek dostu dizi yapısı nedeniyle çoğu zaman en hızlısıdır.

Java ve Uygulamalar

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/poll O(log n); peek, size O(1); remove(nesne) ve contains O(n).
  • Thread-safe değildir; çok iş parçacıklı kullanımda PriorityBlockingQueue tercih edilir.
  • Eşit öncelikte FIFO garantisi yoktur.

Java ve Uygulamalar · Kodu adım adım çalıştır

Kod İzleme: PriorityQueue içeride ne yapar?

new PriorityQueuesize = 00123

Boş bir PriorityQueue: içeride bir dizi (queue) üzerinde ikili min-heap tutar; kökte en küçük öğe.

50offer(5)size = 105123

offer(5): heap boştu; 5 köke (indis 0) yerleşir.

50ebeveyn11yenioffer(1)size = 2051123▲ yeni▲ ebeveyn

offer(1): indis 1'e eklenir; ebeveyni 5 > 1 → min-heap sırası bozuk.

10kök51offer(1)size = 2011523▲ kök

siftUp (up-heap): 1 yukarı çıkar ve köke ulaşır → queue = [1, 5]. Maliyet O(log n).

10ebeveyn5182yenioffer(8)size = 30115283▲ yeni▲ ebeveyn

offer(8): indis 2'ye eklenir; ebeveyn 1 ≤ 8 → up-heap gerekmez.

10kök5182peek()size = 30115283▲ kök

peek(): kökteki 1 döner ama çıkarılmaz → O(1).

8051poll()size = 2081523

poll(): kök 1 alınır; son öğe 8 köke taşınır, size = 2.

5081poll()size = 2051823

siftDown (down-heap): 8 küçük çocukla yer değiştirir → queue = [5, 8]. Konsola 1 yazılır.

80poll()size = 108123

poll(): kök 5 alınır; son öğe 8 köke taşınır, size = 1.

80poll()size = 108123

Tek öğe kaldı; siftDown gerekmez. Konsola 5 yazılır.

80size()size = 108123

size() = 1 (yalnızca 8 kaldı) → O(1). Konsol çıktısı: 1 (peek), 1, 5 (poll), 1 (size).

PriorityQueue<Integer> pq =
    new PriorityQueue<>();        // min-heap
pq.offer(5);
pq.offer(1);
pq.offer(8);
System.out.println(pq.peek());    // bakar
System.out.println(pq.poll());    // çıkarır
System.out.println(pq.poll());
System.out.println(pq.size());

Java ve Uygulamalar · Kodu adım adım çalıştır

Uygulama: kinciEnBuyuk([5, 1, 9, 3, 7, 6], 3)

a501192337465pq (min-heap)(boş)queue[]size = 00123kök = k büyüğün en küçüğü

k = 3: boyutu en çok k olan bir min-heap tutacağız. Kökte, o ana dek görülen en büyük k öğenin en küçüğü durur.

a501192337465▲ xpq (min-heap)5queue[]size = 150123kök = k büyüğün en küçüğü

x = 5: offer(5) → sona eklenir, up-heap. size = 1.

a501192337465▲ xpq (min-heap)5queue[]size = 150123kök = k büyüğün en küçüğü

size = 1 ≤ k = 3 → henüz atılacak öğe yok.

a501192337465▲ xpq (min-heap)15queue[]size = 2105123kök = k büyüğün en küçüğü

x = 1: offer(1) → sona eklenir, up-heap (yukarı yüzdü: 1 kaydırma). size = 2.

a501192337465▲ xpq (min-heap)15queue[]size = 2105123kök = k büyüğün en küçüğü

size = 2 ≤ k = 3 → henüz atılacak öğe yok.

a501192337465▲ xpq (min-heap)159queue[]size = 31051923kök = k büyüğün en küçüğü

x = 9: offer(9) → sona eklenir, up-heap. size = 3.

a501192337465▲ xpq (min-heap)159queue[]size = 31051923kök = k büyüğün en küçüğü

size = 3 ≤ k = 3 → henüz atılacak öğe yok.

a501192337465▲ xpq (min-heap)1395queue[]size = 410319253kök = k büyüğün en küçüğü

x = 3: offer(3) → sona eklenir, up-heap (yukarı yüzdü: 1 kaydırma). size = 4.

a501192337465▲ xpq (min-heap)359queue[]size = 33051923atılan: 1kök = k büyüğün en küçüğü

size = 4 > k → poll(): en küçük 1 atılır (k büyüğe giremez). Son öğe köke, down-heap.

a501192337465▲ xpq (min-heap)3597queue[]size = 430519273kök = k büyüğün en küçüğü

x = 7: offer(7) → sona eklenir, up-heap. size = 4.

a501192337465▲ xpq (min-heap)579queue[]size = 35071923atılan: 3kök = k büyüğün en küçüğü

size = 4 > k → poll(): en küçük 3 atılır (k büyüğe giremez). Son öğe köke, down-heap.

a501192337465▲ xpq (min-heap)5697queue[]size = 450619273kök = k büyüğün en küçüğü

x = 6: offer(6) → sona eklenir, up-heap (yukarı yüzdü: 1 kaydırma). size = 4.

a501192337465▲ xpq (min-heap)679queue[]size = 36071923atılan: 5kök = k büyüğün en küçüğü

size = 4 > k → poll(): en küçük 5 atılır (k büyüğe giremez). Son öğe köke, down-heap.

a501192337465pq (min-heap)679queue[]size = 36071923kök = k büyüğün en küçüğü

Döngü bitti: pq = en büyük üç öğe {6, 7, 9}. peek() = 6 → 3'üncü en büyük. Toplam O(n log k), bellek O(k).

static int kinciEnBuyuk(int[] a, int k) {
  PriorityQueue<Integer> pq = new PriorityQueue<>();
  for (int x : a) {
    pq.offer(x);                    // O(log k)
    if (pq.size() > k) pq.poll();  // en küçüğü at
  }
  return pq.peek();       // k büyüğün en küçüğü
}

Java ve Uygulamalar

Uygulama: Öncelikli Çizelgeleyici

Yüksek (0)P4Orta (1)P1P5Düşük (2)P2P3önce en yüksekdolu seviyeCPU
Kesin öncelikCPU her zaman en yüksek öncelikli, boş olmayan kuyruğun önündeki süreci seçer.
Seviye içinde FIFOAynı öncelikteki süreçler kendi kuyruklarında geliş sırasıyla (gerekirse round robin) çalışır.
Açlık ve yaşlandırmaDüşük öncelikliler hiç çalışamayabilir (starvation). Bekledikçe önceliği artırmak (aging) çözümdür.

Simülatör: Priority Scheduler (üç seviye, quantum, isteğe bağlı yaşlandırma).

Özet

Özet

Öncelikli kuyrukinsert, min, removeMin. Öğeler öncelik sırasıyla çıkar.
Heap özelliğiTam ikili ağaç + ebeveyn ≥ çocuk (max) veya ≤ çocuk (min).
Dizi gösterimiçocuklar 2i+1, 2i+2 · ebeveyn ⌊(i−1)/2⌋
İşlemHeapNasıl?
peek (max/min)O(1)heap[0]
ekleO(log n)heap[n]'e koy, up-heap
silMax / removeMinO(log n)son öğeyi köke taşı, down-heap
heap oluşturmaO(n)⌊n/2⌋ − 1'den 0'a down-heap
heap sortO(n log n)oluştur + n − 1 kez kökü sona al

Özet

Etkileşimli Simülatörler

Heap simülatörüEkleme, en büyüğü/en küçüğü çıkarma; ağaç ve dizi görünümünde up-heap ve down-heap adımları.
Öncelikli kuyruk simülatörüÖncelikli öğe ekleme ve çıkarmayla ADT davranışı.
Öncelikli görev çizelgeleyiciİşletim sistemi tarzı öncelikli çizelgeleme.

Özet

Sık Yapılan Hatalar

"Heap sıralıdır"Değildir; yalnızca kök bellidir. 3, 1, 2 eklenen PriorityQueue için println(pq) → [1, 3, 2].
İndis formülleri0 tabanlı: 2i+1, 2i+2, (i−1)/2. 1 tabanlı: 2i, 2i+1, i/2. İkisini karıştırmak ağacı bozar.
Sağ çocuk sınırıj + 1 < n kontrolü unutulursa heap dışındaki (silinmiş) öğe okunur.
Yanlış çocukMax-heap'te down-heap büyük çocukla takas eder; küçük çocuk seçilirse sıra bozulur.
Heap'te aramacontains ve remove(nesne) O(n)'dir. Hızlı erişim için konum (locator) ya da eşleme gerekir.
Yön karışıklığıPriorityQueue varsayılan min-heap'tir; max için Comparator.reverseOrder().

Özet

Örnek Problemler

  1. Boş bir max-heap'e sırasıyla 5, 12, 8, 3, 15, 10 ekleyin. Son dizi nedir?
  2. 1. sorudaki heap'e bir kez silMax() uygulayın. Dönen değer ve yeni dizi nedir?
  3. {3, 1, 6, 5, 2, 4} dizisini aşağıdan yukarıya yöntemle max-heap'e çevirin.
  4. 0 tabanlı dizide indis 13'teki düğümün ebeveyni ve çocukları hangi indislerdedir?
  5. {10, 7, 9, 8, 2, 5} bir max-heap midir? Değilse neden?
  6. Bir min-heap'teki en büyük öğeyi bulmanın maliyeti nedir?
Cevapları göster
  1. {15, 12, 10, 3, 5, 8}
  2. 15 döner; yeni dizi {12, 8, 10, 3, 5}.
  3. {6, 5, 4, 1, 2, 3}
  4. Ebeveyn ⌊(13 − 1)/2⌋ = 6; çocuklar 27 ve 28.
  5. Hayır: indis 1'deki 7'nin çocuğu 8 (indis 3) ondan büyük.
  6. O(n): en büyük öğe yapraklardan biridir ve yaklaşık n/2 yaprağın hepsine bakmak gerekir.

Özet

Alıştırmalar

  1. [4, 10, 3, 5, 1] dizisine heap sort uygulayın; her adımdan sonra dizi nedir?
  2. 100 öğeli bir ikili heap'in yüksekliği nedir? Bir eklemede en çok kaç takas yapılır?
  3. Farklı anahtarlı max-heap'te 2. ve 3. en büyük öğe hangi indislerde olabilir?
  4. heapMi yordamını min-heap için yeniden yazın. Boş ve tek öğeli dizide ne döner?
  5. Toplam N öğeli k sıralı liste min-heap ile nasıl birleştirilir? Maliyet?
  6. [9, 7, 8, 3, 5, 6, 4] max-heap'inde anahtarDegistir(5, 10) sonrası dizi nedir?
Cevap ve ipuçları
  1. Oluşturma: [10, 5, 3, 4, 1]; sonra (| sağı sıralı): [5, 4, 3, 1 | 10] → [4, 1, 3 | 5, 10] → [3, 1 | 4, 5, 10] → [1 | 3, 4, 5, 10].
  2. ⌊log₂ 100⌋ = 6; en çok 6 takas (yaprak → kök).
  3. 2. en büyük kökün çocuğudur: indis 1 ya da 2. 3. en büyük: indis 1–6.
  4. if (a[e] > a[i]) return false; Döngü hiç dönmez → ikisinde de true.
  5. Her listenin başını heap'e koy; en küçüğü çıkar, onun listesinden sonrakini ekle. Heap ≤ k öğe → O(N log k).
  6. 6 → 10, up-heap: 10 önce 8, sonra 9 ile takas → [10, 7, 9, 3, 5, 8, 4].

Ö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 PriorityQueue API belgesi.

Son

Bölüm 11: Öncelikli Kuyruklar ve Heap
Sonraki sunum: Bölüm 12: Eşlemeler ve Hash Tabloları · Çalışma soruları · Simülatörler: Binary Heap Simulator, Priority Queue Simulator, Priority Scheduler Simulator · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.