**Advice I - `where TPriority : notnull`** I would add `where TPriority : notnull` to the class declaraction, since the priority keys are always required. **Note I** I like your hack: BubbleUp(index); BubbleDown(index); At most one of the two above calls will actually bubble up or down. By the way, it is conventional to call `BubbleX` as `SiftX`. **Advice II - performance** You could squeeze some CPU cycles by relying on an array of heap nodes, each heap node containing the item, priority and the index of the item, instead of a `List`. **Summa summarum** All in all, I had the following rewrite in mind: ``` namespace CR.PriorityQueues { class CoderoddeBinaryHeap<TElement, TPriority> where TElement : notnull where TPriority : notnull { private static readonly int INITIAL_CAPACITY = 16; private class HeapNode<Element, Priority> where Element : notnull where Priority : notnull { public Element element; public Priority priority; public int index; public HeapNode(Element element, Priority priority) { this.element = element; this.priority = priority; } public string ToString() { return "[" + element.ToString() + " - " + priority.ToString() + ", index: " + index + "]"; } } private int size; private HeapNode<TElement, TPriority>[] heapNodeArray = new HeapNode<TElement, TPriority>[INITIAL_CAPACITY]; private readonly Dictionary<TElement, HeapNode<TElement, TPriority>> map = new(); public IComparer<TPriority> Comparer { get; } public CoderoddeBinaryHeap() { Comparer = Comparer<TPriority>.Default; } public CoderoddeBinaryHeap(IComparer<TPriority> comparer) { Comparer = comparer; } public int Count => size; public void Enqueue(TElement item, TPriority priority) { if (IsFull()) { extendHeapNodeArray(); } HeapNode<TElement, TPriority> node = new HeapNode<TElement, TPriority>(item, priority); node.index = size; heapNodeArray[size] = node; SiftUp(size); size++; map[item] = node; } public void EnqueueOrUpdate(TElement item, TPriority priority) { if (map.TryGetValue(item, out HeapNode<TElement, TPriority> node)) { node.priority = priority; SiftUp(node.index); SiftDown(node.index); } else { Enqueue(item, priority); } } public bool TryDequeue(out TElement element, out TPriority priority) { if (size == 0) { (element, priority) = (default, default); return false; } HeapNode<TElement, TPriority> node = heapNodeArray[0]; size--; heapNodeArray[0] = heapNodeArray[size]; node.index = 0; SiftDown(0); (element, priority) = (node.element, node.priority); heapNodeArray[size] = null; return true; } private void SiftUp(int index) { HeapNode<TElement, TPriority> node = heapNodeArray[index]; TPriority priority = node.priority; while (index > 0) { int parentIndex = (index - 1) / 2; HeapNode<TElement, TPriority> parentNode = heapNodeArray[parentIndex]; if (Comparer.Compare(priority, parentNode.priority) < 0) { heapNodeArray[index] = parentNode; parentNode.index = index; index = parentIndex; } else { break; } } heapNodeArray[index] = node; node.index = index; } private bool IsFull() { return size == heapNodeArray.Length; } private void extendHeapNodeArray() { HeapNode<TElement, TPriority>[] newHeapNodeArray = new HeapNode<TElement, TPriority>[heapNodeArray.Length * 2]; Array.Copy(heapNodeArray, 0, newHeapNodeArray, 0, size); heapNodeArray = newHeapNodeArray; } private void shrinkHeapNodeArray() { HeapNode<TElement, TPriority>[] newHeapNodeArray = new HeapNode<TElement, TPriority>[heapNodeArray.Length / 2]; Array.Copy(heapNodeArray, 0, newHeapNodeArray, 0, newHeapNodeArray.Length); heapNodeArray = newHeapNodeArray; } private bool shouldShrink() { return heapNodeArray.Length / 2 != INITIAL_CAPACITY && 4 * size <= heapNodeArray.Length; } private bool Compare(int leftIndex, int rightIndex) => Comparer.Compare(heapNodeArray[leftIndex].priority, heapNodeArray[rightIndex].priority) < 0; private void SiftDown(int index) { HeapNode<TElement, TPriority> node = heapNodeArray[index]; TPriority priority = node.priority; while (true) { int minimumChildIndex; int leftChildIndex = 2 * index + 1; if (leftChildIndex < size) { minimumChildIndex = leftChildIndex; } else { heapNodeArray[index] = node; node.index = index; return; } int rightChildIndex = leftChildIndex + 1; if (rightChildIndex < size && Compare(rightChildIndex, leftChildIndex)) { minimumChildIndex = rightChildIndex; } if (Comparer.Compare(priority, heapNodeArray[minimumChildIndex].priority) > 0) { heapNodeArray[index] = heapNodeArray[minimumChildIndex]; heapNodeArray[index].index = index; index = minimumChildIndex; leftChildIndex = index * 2 + 1; rightChildIndex = leftChildIndex + 1; } else { heapNodeArray[index] = node; node.index = index; return; } } } } } ``` (The entire benchmark program is in [my gist](https://gist.github.com/coderodde/5df03dc69ebee96b5f4aa6f6aef2130b).) ## Typical output ## ``` Seed = -368462730. MyPriorityQueue`2.Enqueue() in 117 milliseconds. MyPriorityQueue`2.EnqueueOrUpdate() in 474 milliseconds. MyPriorityQueue`2.TryDequeue() in 2176 milliseconds. Total MyPriorityQueue`2 duration: 2767 milliseconds. CoderoddeBinaryHeap`2.Enqueue() in 288 milliseconds. CoderoddeBinaryHeap`2.EnqueueOrUpdate() in 195 milliseconds. CoderoddeBinaryHeap`2.TryDequeue() in 1010 milliseconds. Total CoderoddeBinaryHeap`2 duration: 1493 milliseconds. Algorithms agree: True. MyPriorityQueue`2 is sorted: True. CoderoddeBinaryHeap`2 is sorted: True. ```