See all coding puzzles and patterns here.
Data Structure Description
A heap is a tree-like data structure that satisfies the heap property, which states that for a min (max) heap, for every element $E$, if $P$ is a parent of the element, then $P$ is less (greater) than $E$.
There are several variants of heaps, but the most common are binary heaps, which can be and typically are implemented using just an array or list, by enforcing that for every index $i$, its direct children are $2i + 1$ and $2i + 2$, which satisfies the heap property if the elements at $2i + 1$ and $2i + 2$ are greater than the element at $i$. In particular, a sorted array already is a binary heap.
A heap can be considered a type of priority queue in which the comparison of items constitutes their relative priorities.
Interface
A min-heap (max-heap) has these these operations:
- a constructor that can take an unsorted array of numbers,
- a
pop
method that removes the lowest (highest) element while keeping the ordering in place, - an
insert
method that adds an element into the heap, and - an optional
pop_push
method that pops the lowest (highest) element and inserts a given element into the heap.
Binary Heap Performance Characteristics
Let $n$ equal the number of elements in the heap. Then runtime complexity should be:
- Insert: $O(log(n))$
- Pop: $O(log(n))$
- Insert all: $O(n \cdot log(n))$
- Pop all: $O(n \cdot log(n))$
Example MinHeap Implementation
class MinHeap(object):
def __init__(self, items: list[int] = []):
self.store = []
for item in items:
self.push(item)
def push(self, item) -> bool:
self.store.append(item)
curr_idx = len(self.store) - 1
parent_idx = MinHeap.get_parent(curr_idx)
while parent_idx >= 0 and parent_idx < curr_idx and self.store[parent_idx] > self.store[curr_idx]:
self.store[parent_idx], self.store[curr_idx] = self.store[curr_idx], self.store[parent_idx]
curr_idx = parent_idx
parent_idx = MinHeap.get_parent(curr_idx)
@classmethod
def get_left(cls, i: int) -> int:
return (2 * i) + 1
@classmethod
def get_right(cls, i: int) -> int:
return (2 * i) + 2
@classmethod
def get_parent(cls, i: int) -> int:
return (i - 1) // 2
def pop(self):
if len(self.store) == 0:
return None
if len(self.store) == 1:
return self.store.pop()
res = self.store[0]
self.store[0] = self.store.pop()
curr_idx = 0
while curr_idx < len(self.store):
left_idx = MinHeap.get_left(curr_idx)
right_idx = MinHeap.get_right(curr_idx)
min_idx = curr_idx
if left_idx < len(self.store) and self.store[left_idx] < self.store[min_idx]:
min_idx = left_idx
if right_idx < len(self.store) and self.store[right_idx] < self.store[min_idx]:
min_idx = right_idx
if min_idx == curr_idx:
break
self.store[curr_idx], self.store[min_idx] = self.store[min_idx], self.store[curr_idx]
curr_idx = min_idx
return res
def pop_push(self, item):
if len(self.store) > 0:
if self.store[0] == item:
return
self.pop()
self.push(item)
Puzzles
Puzzle | Difficulty | External Link |
Find the Kth Largest Element | Medium | Leetcode 215 |
K Closest Points to Origin | Medium | Leetcode 973 |
Largest K Elements | Medium | |
Magic Candy Bags | Medium |