using System;
using System.Collections.Generic;
///
/// Implementasi generic Priority Queue (Antrean Prioritas) menggunakan min-heap.
/// Memungkinkan operasi enqueue, dequeue, dan update priority dengan efisien.
///
/// Tipe elemen yang disimpan dalam antrean
/// Tipe prioritas yang digunakan, harus implementasi IComparable
public class PriorityQueue where TPriority : IComparable
{
// Menyimpan pasangan elemen dan prioritasnya dalam struktur heap
private List> elements = new List>();
// Menyimpan indeks setiap elemen dalam heap untuk akses cepat
private Dictionary elementIndexMap = new Dictionary();
///
/// Mendapatkan jumlah elemen dalam antrean prioritas
///
public int Count => elements.Count;
///
/// Menambahkan elemen baru ke dalam antrean prioritas dengan prioritas tertentu
///
/// Elemen yang akan ditambahkan
/// Prioritas dari elemen
public void Enqueue(TElement element, TPriority priority)
{
elements.Add(Tuple.Create(element, priority));
elementIndexMap[element] = elements.Count - 1;
HeapifyUp(elements.Count - 1);
}
///
/// Mengambil dan menghapus elemen dengan prioritas tertinggi (nilai terkecil)
/// dari antrean prioritas
///
/// Elemen dengan prioritas tertinggi
/// Dilempar jika antrean kosong
public TElement Dequeue()
{
if (elements.Count == 0)
throw new InvalidOperationException("The priority queue is empty.");
var element = elements[0].Item1;
var last = elements[elements.Count - 1];
elements.RemoveAt(elements.Count - 1);
if (elements.Count > 0)
{
// Pindahkan elemen terakhir ke root, lalu atur ulang heap
elements[0] = last;
elementIndexMap[last.Item1] = 0;
HeapifyDown(0);
}
elementIndexMap.Remove(element);
return element;
}
///
/// Memperbarui prioritas elemen yang sudah ada dalam antrean
///
/// Elemen yang akan diperbarui prioritasnya
/// Nilai prioritas baru
/// Dilempar jika elemen tidak ditemukan
public void UpdatePriority(TElement element, TPriority newPriority)
{
if (!elementIndexMap.ContainsKey(element))
throw new InvalidOperationException("Element not found in priority queue.");
var index = elementIndexMap[element];
var oldPriority = elements[index].Item2;
elements[index] = Tuple.Create(element, newPriority);
// Jika prioritas baru lebih tinggi (nilai lebih kecil), heapify up
if (newPriority.CompareTo(oldPriority) < 0)
{
HeapifyUp(index);
}
// Jika prioritas baru lebih rendah (nilai lebih besar), heapify down
else
{
HeapifyDown(index);
}
}
///
/// Mempertahankan properti heap dengan memindahkan elemen ke atas jika
/// prioritasnya lebih tinggi dari parent
///
/// Indeks elemen yang akan dipindahkan ke atas
private void HeapifyUp(int index)
{
var parentIndex = (index - 1) / 2;
if (index > 0 && elements[index].Item2.CompareTo(elements[parentIndex].Item2) < 0)
{
Swap(index, parentIndex);
HeapifyUp(parentIndex);
}
}
///
/// Mempertahankan properti heap dengan memindahkan elemen ke bawah jika
/// prioritasnya lebih rendah dari child
///
/// Indeks elemen yang akan dipindahkan ke bawah
private void HeapifyDown(int index)
{
var leftChildIndex = 2 * index + 1;
var rightChildIndex = 2 * index + 2;
var smallest = index;
// Cari child dengan prioritas tertinggi (nilai terkecil)
if (leftChildIndex < elements.Count && elements[leftChildIndex].Item2.CompareTo(elements[smallest].Item2) < 0)
{
smallest = leftChildIndex;
}
if (rightChildIndex < elements.Count && elements[rightChildIndex].Item2.CompareTo(elements[smallest].Item2) < 0)
{
smallest = rightChildIndex;
}
// Jika child memiliki prioritas lebih tinggi, tukar dan lanjutkan heapify down
if (smallest != index)
{
Swap(index, smallest);
HeapifyDown(smallest);
}
}
///
/// Menukar posisi dua elemen dalam heap dan memperbarui elementIndexMap
///
/// Indeks elemen pertama
/// Indeks elemen kedua
private void Swap(int i, int j)
{
var temp = elements[i];
elements[i] = elements[j];
elements[j] = temp;
// Perbarui elementIndexMap untuk mencerminkan posisi baru
elementIndexMap[elements[i].Item1] = i;
elementIndexMap[elements[j].Item1] = j;
}
}