İkili heap: dizi gösterimi, percolate, buildHeap, heapsort
Binary heap'in dizi içinde tutulması, insert (percolate up) ve deleteMin (percolate down) izleri, O(n) buildHeap ve heapsort. Sınavın klasik sorusu: şu işlemlerden sonra dizi neye benzer?
Heap, öncelik kuyruğunun (priority queue) arkasındaki yapıdır: en küçük (ya da en büyük) elemana her an O(1)‘de ulaşırsın, ekleme ve silme O(log n). Ağaç gibi düşünülür ama dizi içinde tutulur, işaretçi yoktur. Sınavda neredeyse hep aynı soru gelir: “Şu sırayla ekle / şu kadar deleteMin yap / buildHeap uygula, son diziyi yaz.” Bu sayfadaki iki kaydırma işlemini (percolate up ve percolate down) doğru yapan biri bu soruları kaçırmaz.
Altın kurallar:
- Yapı kuralı: heap tam (complete) ikili ağaçtır. Seviyeler soldan sağa doldurulur, boşluk yok.
- Sıra kuralı (min-heap): her düğüm çocuklarından küçük ya da eşittir. En küçük eleman kökte.
- Ekleme yukarı, silme aşağı. insert sona koyar ve yukarı kaydırır. deleteMin son elemanı köke koyar ve aşağı kaydırır.
- Aşağı inerken küçük çocukla yer değiştir. Büyük çocukla değiştirirsen sıra kuralı bozulur.
Dizi gösterimi
Ağaç tam olduğu için düğümleri seviye sırasıyla diziye yazmak yeterli. Bu derste (ve Weiss’te) öncelik kuyruğu heap’i 1. indisten başlar, a[0] boş kalır. O zaman formüller çok temiz olur:
| İstenen | Formül (1-indisli) | 0-indisli karşılığı |
|---|---|---|
| ebeveyn | i / 2 | (i - 1) / 2 |
| sol çocuk | 2i | 2i + 1 |
| sağ çocuk | 2i + 1 | 2i + 2 |
index: 1 2 3 4 5 6 7 8
array: [6, 14, 9, 20, 17, 11, 30, 25]
6
/ \
14 9
/ \ / \
20 17 11 30
/
25
25’in indisi 8, ebeveyni 8 / 2 = 4, yani 20. Her ebeveyn çocuklarından küçük: bu bir min-heap. Ama kardeşler arasında (14 ile 9 gibi) bir sıra yok. Heap, BST değildir.
n elemanlı heap’in yüksekliği . 8 eleman için 3.
insert: percolate up
- Yeni elemanı ilk boş yere (dizinin sonuna, index size + 1) koy.
- Ebeveyninden küçük olduğu sürece ebeveyniyle yer değiştir.
Yukarıdaki heap’e 8 ekleyelim:
8 index 9'a: ebeveyn a[4]=20 > 8 → yukari
8 index 4'te: ebeveyn a[2]=14 > 8 → yukari
8 index 2'de: ebeveyn a[1]=6 < 8 → dur
[6, 8, 9, 14, 17, 11, 30, 25, 20]
6
/ \
8 9
/ \ / \
14 17 11 30
/ \
25 20
En kötü durumda yeni eleman köke kadar çıkar: O(log n).
deleteMin: percolate down
- Kökteki elemanı (minimum) sakla, döndüreceğiz.
- Son elemanı köke taşı, boyutu 1 azalt.
- Kökten başlayarak: iki çocuktan küçük olanı seç. Çocuk taşıdığın değerden küçükse yer değiştir, aşağı in. Değilse dur.
Az önceki 9 elemanlı heap’te deleteMin:
6 cikar, son eleman 20 koke:
[20, 8, 9, 14, 17, 11, 30, 25]
i=1: cocuklar 8, 9 → 8 < 20, degistir
i=2: cocuklar 14, 17 → 14 < 20, degistir
i=4: cocuk 25 (index 8) > 20 → dur
[8, 14, 9, 20, 17, 11, 30, 25]
TUZAK · Silinen elemanın yerine son eleman gelir. Köke “küçük çocuğu çıkarmak” diye bir şey yapılmaz. Önce son eleman köke konur, sonra aşağı kaydırılır. Böylece ağaç tam kalır, boşluk oluşmaz.
buildHeap: O(n) kurulum
Elinde sırasız n eleman varsa iki yol var: n kez insert (O(n log n)) ya da Floyd yöntemi. Floyd: elemanları olduğu gibi diziye koy, sonra son iç düğümden (n/2) köke doğru her düğümde percolateDown çağır. Yapraklar (n/2’den sonrakiler) zaten tek başına heap’tir, onlara dokunulmaz.
[15, 12, 10, 8, 4, 6, 2, 9, 1, 3] için (n = 10, i = 5, 4, 3, 2, 1):
percDown(5) -> [15, 12, 10, 8, 3, 6, 2, 9, 1, 4]
percDown(4) -> [15, 12, 10, 1, 3, 6, 2, 9, 8, 4]
percDown(3) -> [15, 12, 2, 1, 3, 6, 10, 9, 8, 4]
percDown(2) -> [15, 1, 2, 8, 3, 6, 10, 9, 12, 4]
percDown(1) -> [1, 3, 2, 8, 4, 6, 10, 9, 12, 15]
result: [1, 3, 2, 8, 4, 6, 10, 9, 12, 15] moves=8
Son adımda 15 kökten en alta (index 10) kadar iner: 1, 3 ve 4 birer seviye yukarı çıkar.
Neden O(n)? percolateDown bir düğümde en fazla o düğümün yüksekliği kadar iş yapar. Düğümlerin yarısı yaprak (iş 0), çeyreği yükseklik 1, sekizde biri yükseklik 2, … Toplam:
Yani iş düğümlerin çoğunun bulunduğu altta az, düğümlerin az olduğu üstte çok. Ortalama sabit, toplam O(n). insert ile kurarken tersi olur: elemanların çoğu altta ve her biri köke kadar çıkabilir.
Java’da MinHeap
public class MinHeap {
private int[] array = new int[11]; // array[0] kullanilmaz
private int size = 0;
public void insert(int x) {
if (size + 1 == array.length)
array = java.util.Arrays.copyOf(array, array.length * 2);
int hole = ++size;
while (hole > 1 && x < array[hole / 2]) {
array[hole] = array[hole / 2]; // ebeveyni asagi al
hole /= 2;
}
array[hole] = x;
}
public int findMin() { return array[1]; }
public int deleteMin() {
if (size == 0) throw new RuntimeException("empty heap");
int min = array[1];
array[1] = array[size--]; // son eleman koke
percolateDown(1);
return min;
}
private void percolateDown(int hole) {
int tmp = array[hole];
while (hole * 2 <= size) {
int child = hole * 2;
if (child != size && array[child + 1] < array[child]) child++;
if (array[child] < tmp) array[hole] = array[child];
else break;
hole = child;
}
array[hole] = tmp;
}
public void buildHeap(int[] items) {
array = new int[items.length + 1];
size = items.length;
for (int i = 0; i < items.length; i++) array[i + 1] = items[i];
for (int i = size / 2; i > 0; i--) percolateDown(i);
}
public String toString() { // array[1..size]
return java.util.Arrays.toString(
java.util.Arrays.copyOfRange(array, 1, size + 1));
}
}
Kod değerleri her adımda takas etmez. Bir “delik” (hole) gezdirir ve elemanı en sonda bir kez yerine yazar. Takas 3 atama, delik kaydırma 1 atama yapar.
MinHeap h = new MinHeap();
for (int x : new int[]{6, 14, 9, 20, 17, 11, 30, 25}) h.insert(x);
h.insert(8);
System.out.println("+8 -> " + h);
System.out.println("deleteMin = " + h.deleteMin() + " -> " + h);
+8 -> [6, 8, 9, 14, 17, 11, 30, 25, 20]
deleteMin = 6 -> [8, 14, 9, 20, 17, 11, 30, 25]
Heapsort
Heap ile sıralama: diziyi max-heap yap, sonra en büyüğü (kök) sona taşı ve heap’i bir küçült. Ek dizi gerekmez. Heapsort’ta dizi 0. indisten başlar (sıralanacak dizi zaten böyle), çocuklar 2i + 1 ve 2i + 2.
- buildMaxHeap: i = n/2 - 1’den 0’a, her birinde (max için) percolateDown.
- n - 1 kez: a[0] ile heap’in son elemanını takasla, heap boyunu 1 azalt, a[0]‘ı aşağı kaydır.
[4, 10, 3, 5, 1] için:
== heapsort [4, 10, 3, 5, 1]
after buildMaxHeap: [10, 5, 3, 4, 1]
after deleteMax #1: [5, 4, 3, 1, 10] (heap part size 4)
after deleteMax #2: [4, 1, 3, 5, 10] (heap part size 3)
after deleteMax #3: [3, 1, 4, 5, 10] (heap part size 2)
after deleteMax #4: [1, 3, 4, 5, 10] (heap part size 1)
Her adımda dizinin sağında sıralı kısım bir büyür. buildMaxHeap: i=1 (10) çocukları 5, 1’den büyük, değişmez; i=0 (4) çocukları 10, 3, büyüğü 10 yukarı, 4 index 1’de çocukları 5, 1, büyüğü 5 yukarı. Sonuç [10, 5, 3, 4, 1].
| Özellik | Heapsort |
|---|---|
| Zaman | O(n log n) en iyi, ortalama, en kötü |
| Ek bellek | O(1), yerinde (in-place) |
| Kararlı mı? | Hayır (eşit anahtarların sırası değişebilir) |
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
| Heap BST gibi sıralıdır, sol < sağ | Sadece ebeveyn-çocuk sırası var; kardeşler serbest |
| deleteMin’de küçük çocuk köke çıkar, boşluk aşağıda kalır | Son eleman köke konur, aşağı kaydırılır; ağaç tam kalır |
| Aşağı kaydırırken ilk (sol) çocukla değiştiririm | İki çocuğun küçüğüyle değiştir (max-heap’te büyüğüyle) |
| buildHeap = n kez insert | Floyd: n/2’den 1’e percolateDown; O(n) ve farklı dizi verebilir |
| buildHeap kökten başlar | Son iç düğümden (n/2) köke doğru gider |
| 1-indisli dizide çocuklar 2i+1, 2i+2 | O 0-indisli formül; 1-indisli: 2i, 2i+1 |
| Heap’te arama O(log n) | Herhangi bir elemanı aramak O(n); sadece min O(1) |
| Heapsort kararlıdır | Değildir; kararlı O(n log n) istiyorsan merge sort |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
In a binary heap stored in an array starting at index 1, the node at index 13 has which parent index and which child indices?
Cevabı göster
1-indisli dizide: ebeveyn , sol çocuk , sağ çocuk .
13 / 2 = 6 (tam sayı bölmesi), 2·13 = 26, 2·13 + 1 = 27. Bu çocuklar ancak dizi boyutu en az 26 ise vardır.
Which array (index 0 unused, listed from index 1) is a valid min-heap?
- [2, 5, 3, 8, 6, 4, 9]
- [2, 5, 3, 4, 6, 7, 9]
- [2, 5, 3, 8, 6, 1, 9]
Cevabı göster
Her i >= 2 için a[i] >= a[i/2] olmalı.
İkincisinde a[4] = 4, ebeveyni a[2] = 5'ten küçük: bozuk.
Üçüncüsünde a[6] = 1, ebeveyni a[3] = 3'ten küçük: bozuk.
Birincisinde 5, 3 >= 2; 8, 6 >= 5; 4, 9 >= 3. Geçerli.
What is the height of a binary heap containing 100 elements? (A single node has height 0.)
Cevabı göster
Heap tam (complete) ikili ağaçtır, yüksekliği . , yani 6.
Give the time complexity of each min-heap operation: findMin, insert, deleteMin, buildHeap (n items).
Cevabı göster
En küçük eleman hep a[1]'de: O(1). insert ve deleteMin en fazla yükseklik kadar (log n) adım kaydırır.
buildHeap, n kez insert yapsaydı O(n log n) olurdu; Floyd yöntemiyle (alttan percolate down) toplam iş düğümlerin yükseklikleri toplamıdır ve bu O(n).
Insert 10, 4, 7, 1, 8, 3 one at a time into an empty min-heap. Show the array (from index 1) after all insertions.
Cevabı göster
+10: [10]. +4: index 2, ebeveyn 10 büyük, yukarı: [4, 10].
+7: index 3, ebeveyn 4 küçük, kalır: [4, 10, 7].
+1: index 4, ebeveyn 10 → yukarı (index 2), ebeveyn 4 → yukarı (index 1): [1, 4, 7, 10].
+8: index 5, ebeveyn 4 küçük: [1, 4, 7, 10, 8].
+3: index 6, ebeveyn 7 → yukarı (index 3), ebeveyn 1 küçük, dur: [1, 4, 3, 10, 8, 7].
Starting from the heap [1, 4, 3, 10, 8, 7], perform deleteMin twice. Show the array after both operations.
Cevabı göster
1. deleteMin: 1 çıkar, son eleman 7 köke gelir: [7, 4, 3, 10, 8]. Çocuklar 4 ve 3, küçüğü 3 < 7: yer değiştir. 7 index 3'te, çocuğu yok: [3, 4, 7, 10, 8].
2. deleteMin: 3 çıkar, son eleman 8 köke: [8, 4, 7, 10]. Çocuklar 4, 7, küçüğü 4 < 8: [4, 8, 7, 10]. 8 index 2'de, çocuğu 10 (index 4) büyük, dur.
Apply buildHeap (Floyd's bottom-up method) to the array [9, 6, 8, 2, 5, 7, 1, 3] to make a min-heap. Show the resulting array.
Cevabı göster
n = 8, percolateDown sırasıyla i = 4, 3, 2, 1.
i=4 (2): çocuğu 3, 2 küçük, değişmez.
i=3 (8): çocuklar 7, 1, küçüğü 1: [9, 6, 1, 2, 5, 7, 8, 3].
i=2 (6): çocuklar 2, 5 → 2 yukarı; 6 index 4'te, çocuğu 3 < 6 → 3 yukarı: [9, 2, 1, 3, 5, 7, 8, 6].
i=1 (9): çocuklar 2, 1 → 1 yukarı; 9 index 3'te, çocuklar 7, 8 → 7 yukarı: [1, 2, 7, 3, 5, 9, 8, 6].
Insert the same keys 9, 6, 8, 2, 5, 7, 1, 3 one at a time into an empty min-heap. Show the final array. Is it the same as the buildHeap result [1, 2, 7, 3, 5, 9, 8, 6]?
Cevabı göster
Tek tek eklemede her yeni eleman yukarı kaydırılır. İz: [9] → [6, 9] → [6, 9, 8] → [2, 6, 8, 9] → [2, 5, 8, 9, 6] → [2, 5, 7, 9, 6, 8] → [1, 5, 2, 9, 6, 8, 7] → +3: index 8, ebeveyn 9 → yukarı, ebeveyn 5 → yukarı, ebeveyn 1 dur: [1, 3, 2, 5, 6, 8, 7, 9].
İki yöntem de geçerli bir heap verir ama diziler farklı. Sınavda hangi yöntemin istendiğine dikkat et.
Insert 20, 15, 30, 10, 40 one at a time into an empty max-heap (index 1 based). Show the final array.
Cevabı göster
Max-heap'te ebeveyn çocuktan büyük ya da eşit olmalı; yeni eleman ebeveyninden büyükse yukarı çıkar.
[20] → [20, 15] → +30: ebeveyn 20 küçük, yukarı: [30, 15, 20] → +10: [30, 15, 20, 10] → +40: index 5, ebeveyn 15 → yukarı, ebeveyn 30 → yukarı: [40, 30, 20, 10, 15].
Apply buildHeap to [15, 12, 10, 8, 4, 6, 2, 9, 1, 3] to make a min-heap. Show the resulting array.
Cevabı göster
n = 10, i = 5, 4, 3, 2, 1:
i=5 (4): çocuğu 3 → [15, 12, 10, 8, 3, 6, 2, 9, 1, 4].
i=4 (8): çocuklar 9, 1 → 1 yukarı: [15, 12, 10, 1, 3, 6, 2, 9, 8, 4].
i=3 (10): çocuklar 6, 2 → 2 yukarı: [15, 12, 2, 1, 3, 6, 10, 9, 8, 4].
i=2 (12): çocuklar 1, 3 → 1 yukarı; index 4'te çocuklar 9, 8 → 8 yukarı: [15, 1, 2, 8, 3, 6, 10, 9, 12, 4].
i=1 (15): çocuklar 1, 2 → 1; index 2'de çocuklar 8, 3 → 3; index 5'te çocuğu 4 → 4; 15 index 10'a iner: [1, 3, 2, 8, 4, 6, 10, 9, 12, 15].
Heapsort (max-heap, array indexed from 0, ascending order) is applied to [5, 2, 9, 1, 7, 3]. Show the array after building the max-heap, and after each of the first two deleteMax steps.
Cevabı göster
buildMaxHeap (0-indisli, çocuklar 2i+1, 2i+2), i = 2, 1, 0:
i=2 (9): çocuğu 3, değişmez. i=1 (2): çocuklar 1, 7 → 7 yukarı: [5, 7, 9, 1, 2, 3]. i=0 (5): çocuklar 7, 9 → 9 yukarı; 5 index 2'de, çocuğu 3 küçük: [9, 7, 5, 1, 2, 3].
deleteMax 1: a[0]=9 ile a[5]=3 yer değiştirir, heap boyu 5: [3, 7, 5, 1, 2 | 9]. 3 aşağı: çocuklar 7, 5 → 7; index 1'de çocuklar 1, 2 küçük: [7, 3, 5, 1, 2, 9].
deleteMax 2: 7 ile a[4]=2 değişir, boy 4: [2, 3, 5, 1 | 7, 9]. 2 aşağı: çocuklar 3, 5 → 5; index 2'nin çocuğu yok: [5, 3, 2, 1, 7, 9].
Devamı: [3, 1, 2, 5, 7, 9] → [2, 1, 3, 5, 7, 9] → [1, 2, 3, 5, 7, 9].
Write the private method percolateDown(int hole) of a min-heap stored in int[] array (index 1 based) with size elements.
Cevabı göster
Değeri her adımda takas etmek yerine "delik" (hole) aşağı kaydırılır: daha az atama yapılır.
private void percolateDown(int hole) {
int tmp = array[hole];
while (hole * 2 <= size) { // en az sol cocuk var
int child = hole * 2;
if (child != size && array[child + 1] < array[child])
child++; // kucuk cocugu sec
if (array[child] < tmp)
array[hole] = array[child]; // cocuk yukari
else
break;
hole = child;
}
array[hole] = tmp;
}Sık hatalar: child != size kontrolünü unutup olmayan sağ çocuğa bakmak, ya da küçük yerine büyük çocuğu seçmek (o max-heap olur).Doğrulama: Bütün dizi durumları (insert, deleteMin, buildHeap adımları, heapsort ara durumları, max-heap ekleme) ve heap kontrolü Java ile üretildi (verify/SEN2212/W07.java, java W07.java). Sayfadaki MinHeap sınıfı aynı dosyada çalıştırıldı ve elle yazılan ikinci uygulamayla aynı dizileri verdi.