Binary Heap
A binary heap is a special type of Binary Search Tree where we can find the smallest (min-heap) or highest (max-heap) value instantly - it’s the root node.
It has the same rules for adding a node as a BST, with two additional rules:
- A parent node must be greater than (or smaller than) both child nodes - this is known as the Heap Property
- Each level of a tree must be filled up completely, except the last level which must be filled from left to right
Useful when you frequently work with the smallest or largest value in a set, but otherwise searching, insertion and deletion take the same amount of time.
Array Representations
Given a Min Heap:
graph TB
8 --> 12 & 23
12 --> 17 & 31
17 --> 102 & 18
23 --> 30 & 44
This can be represented with an array:
[8, 12, 23, 17, 31, 30, 44, 102, 18]
To calculate the children of a current node at index :
child one = child two =
To calculate the parent index of any given node:
parent = floor
In Kotlin
Use the ProrityQueue
interface:
val minHeap = PriorityQueue<ListNode> { a, b ->
a.value - b.value
}
val maxHeap = PriorityQueue<ListNode> { a, b ->
b.value - a.value
}
while (heap.isNotEmpty()) {
val node = heap.poll()
// Do something here
}
Usecase
Classically, these are used to merge k
Linked Lists in time, where k
is the number of Linked Lists.
fun mergeKLists(lists: Array<ListNode?>): ListNode? {
val heap = PriorityQueue<ListNode> { a, b ->
a.value - b.value
}
for (list in lists.filterNotNull()) {
var current: ListNode? = list
while (current != null) {
heap.add(current)
current = current.next
}
}
val head = ListNode(0)
var current = head
while (heap.isNotEmpty()) {
val node = heap.poll()
node.next = null
current.next = node
current = current.next!!
}
return head.next
}
Heaps are also used to help find the median value from a stream in time:
- A max heap
lo
stores the lower half of the stream - A min heap
hi
stores the higher half of the stream
class MedianFinder {
// Max heap
private val lo = PriorityQueue<Int> { a, b -> b - a}
// Min heap
private val hi = PriorityQueue<Int> { a, b -> a - b}
fun addNum(num: Int) {
lo.add(num)
hi.add(lo.poll())
if (lo.size < hi.size) {
lo.add(hi.poll())
}
}
fun findMedian(): Double = when {
lo.size > hi.size -> lo.peek().toDouble()
else -> (lo.peek() + hi.peek()) * 0.5
}
}
Complexity
- Insert/Delete
- Poll
- Peek