Randomized Heap

 

Randomized Heap is a variation of the heap data structure where the elements are stored in a binary tree-like structure. In addition to the properties of a regular heap, a Randomized Heap also maintains a random priority for each element, which determines their position in the heap.

The Randomized Heap supports the following operations:

  • insert(value): Inserts a new element with the given value into the heap.
  • delete(value): Deletes the element with the given value from the heap.
  • find_min(): Returns the minimum element in the heap.
  • extract_min(): Removes and returns the minimum element from the heap.
  • merge(heap): Merges the current heap with another heap, combining their elements.
  • is_empty(): Checks if the heap is empty.

To implement a Randomized Heap in Python, we can define a RandomizedHeap class and its associated functions.


import random

class RandomizedHeap:
    def __init__(self):
        self.heap = []

    def insert(self, value):
        self.heap.append(value)
        self._bubble_up(len(self.heap) - 1)

    def delete(self, value):
        if value in self.heap:
            index = self.heap.index(value)
            self._swap(index, len(self.heap) - 1)
            self.heap.pop()
            self._bubble_down(index)

    def find_min(self):
        if self.heap:
            return self.heap[0]
        return None

    def extract_min(self):
        if self.heap:
            self._swap(0, len(self.heap) - 1)
            min_val = self.heap.pop()
            self._bubble_down(0)
            return min_val
        return None

    def merge(self, heap):
        self.heap += heap.heap
        self._build_heap()

    def is_empty(self):
        return len(self.heap) == 0

    def _bubble_up(self, index):
        parent_idx = (index - 1) // 2
        while index > 0 and self.heap[index] < self.heap[parent_idx]:
            self._swap(index, parent_idx)
            index = parent_idx
            parent_idx = (index - 1) // 2

    def _bubble_down(self, index):
        left_child_idx = 2 * index + 1
        right_child_idx = 2 * index + 2

        while left_child_idx < len(self.heap):
            smallest_child_idx = left_child_idx

            if right_child_idx < len(self.heap) and self.heap[right_child_idx] < self.heap[left_child_idx]:
                smallest_child_idx = right_child_idx

            if self.heap[index] <= self.heap[smallest_child_idx]:
                break

            self._swap(index, smallest_child_idx)
            index = smallest_child_idx
            left_child_idx = 2 * index + 1
            right_child_idx = 2 * index + 2

    def _swap(self, i, j):
        self.heap[i], self.heap[j] = self.heap[j], self.heap[i]

    def _build_heap(self):
        n = len(self.heap)
        for i in range(n // 2 - 1, -1, -1):
            self._bubble_down(i)


Now let's solve a question from LeetCode using the Randomized Heap. Consider the problem "Kth Largest Element in an Array" (LeetCode #215), where we need to find the Kth largest element in an unsorted array.


def find_kth_largest(nums, k):
    heap = RandomizedHeap()

    for num in nums:
        heap.insert(num)
        if len(heap.heap) > k:
            heap.extract_min()

    return heap.extract_min()


In this example, we create a RandomizedHeap object and iterate through the elements in the nums array. We insert each element into the heap, and if the size of the heap becomes larger than k, we extract the minimum element from the heap to maintain the heap size as k. Finally, we return the minimum element from the heap, which will be the Kth largest element in the array.

The time complexity of the find_kth_largest function is O(n * log k), where n is the size of the input array. The insert and extract_min operations in the heap both have a time complexity of O(log k), and we perform these operations for each element in the array. The space complexity is O(k) for storing the elements in the Randomized Heap.

 

Next Post Previous Post