Heap
A heap is a specialized tree-based data structure that satisfies the heap property. The heap property specifies that for a heap with a parent node and its children, the value of the parent node is either greater than or equal to (in a max heap) or less than or equal to (in a min heap) the values of its children.
Heaps are commonly used to implement priority queues, where elements with higher priorities are dequeued first. They provide efficient insertion, deletion, and retrieval of the highest-priority element.
In Python, the heapq
module provides functions to work with heaps. However, I'll explain the heap concept and implement it from scratch without using any library.
Here's an example implementation of a min heap in Python:
class MinHeap:
def __init__(self):
self.heap = []
self.size = 0
def parent(self, index):
return (index - 1) // 2
def left_child(self, index):
return 2 * index + 1
def right_child(self, index):
return 2 * index + 2
def swap(self, i, j):
self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
def insert(self, value):
self.heap.append(value)
self.size += 1
self.heapify_up(self.size - 1)
def heapify_up(self, index):
while index > 0 and self.heap[index] < self.heap[self.parent(index)]:
self.swap(index, self.parent(index))
index = self.parent(index)
def extract_min(self):
if self.size == 0:
return None
minimum = self.heap[0]
self.swap(0, self.size - 1)
self.heap.pop()
self.size -= 1
self.heapify_down(0)
return minimum
def heapify_down(self, index):
while True:
left = self.left_child(index)
right = self.right_child(index)
smallest = index
if left < self.size and self.heap[left] < self.heap[smallest]:
smallest = left
if right < self.size and self.heap[right] < self.heap[smallest]:
smallest = right
if smallest == index:
break
self.swap(index, smallest)
index = smallest
Now, let's solve a LeetCode problem using the min heap we implemented. The problem is "Merge k Sorted Lists," where we need to merge k sorted linked lists into a single sorted list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def merge_k_lists(lists):
min_heap = MinHeap()
# Insert the first node from each list into the min heap
for head in lists:
if head:
min_heap.insert(head.val)
# Create a dummy node to build the merged list
dummy = ListNode(0)
current = dummy
# Process the nodes in ascending order using the min heap
while min_heap.size > 0:
min_val = min_heap.extract_min()
current.next = ListNode(min_val)
current = current.next
# Find the next node in the list and insert its value into the min heap
for head in lists:
if head and head.val == min_val:
head = head.next
if head:
min_heap.insert(head.val)
return dummy.next
The time complexity of the insert
operation in the min heap is O(log n), where n is the number of elements in the heap. The extract_min
operation also has a time complexity of O(log n). Since we insert all elements from the input lists into the heap, the overall time complexity of the merge_k_lists
function is O(n log k), where n is the total number of elements across all lists and k is the number of lists.
The space complexity is O(k), as we only store the elements from the k input lists in the min heap.