I've written a min-max heap, i.e. an heap where finding the minimum and the maximum are constant operations. Now I want to create tests for my class, so I decided to implement a function that checks if a heap is a min-max-heap. Here it is, but I'm not sure it's 100% correct.
def is_min_max_heap(h):
if not isinstance(h, MinMaxHeap):
return False
if h.heap:
for item in h.heap:
if not isinstance(item, HeapNode):
return False
for i, item in reversed(list(enumerate(h.heap))):
g = h.grandparent_index(i)
if g is not None:
if h.is_on_even_level(i):
if h.heap[g] > item:
return False
else:
if h.heap[g] < item:
return False
return True
Note that elements of this heap are represented with the class HeapNode
, and that's why I am checking if self.heap
only contains objects of that class. Even levels are for example 0, 2, 4, etc. The minimum of this heap is found at self.heap[0]
. The maximum is max(self.heap[1], self.heap[2])
(provided that both exist). h.grandparent_index(i)
returns None
if the grandparent of the node at i
does not exist.
The idea of my algorithm is very simple. I start from the bottom, and I check if I'm on an even of odd level. If on an even level, then I must make sure that the element is greater than its grandparent. If I'm on a odd level, I must make sure it's smaller than its grandparent.
Is my algorithm correct? Am I missing some points? If it's correct, suggestions to improve it are well-accepted.
Eventually my implementation might be useful for someone else.
Edit 1
I've just noticed that my function checks if the elements in the even (and respectively odd) levels are correctly disposed to each other, but it does not check if the maximum element is found either at self.heap[1]
or self.heap[2]
and that the minimum element is at self.heap[0]
.
Edit 2
I'm adding the new updated code according to edit 1 and to the answer of @goCards.
def is_min_max_heap(h) -> bool:
"""Returns `True` if `h` is a valid `MinMaxHeap` object. `False` otherwise."""
if not isinstance(h, MinMaxHeap):
return False
if h.heap:
for item in h.heap:
if not isinstance(item, HeapNode):
return False
if h.size() == 1:
return True
if h.size() == 2:
return max(h.heap) == h.heap[1] and min(h.heap) == h.heap[0]
if h.size() >= 3:
if (h.heap[0] != min(h.heap) or
(h.heap[1] != max(h.heap) and
h.heap[2] != max(h.heap))):
return False
for i, item in reversed(list(enumerate(h.heap))):
p = h.parent_index(i)
if p != -1:
if h.is_on_even_level(i):
if h.heap[p] < item:
return False
else:
if h.heap[p] > item:
return False
g = h.grandparent_index(i)
if g != -1:
if h.is_on_even_level(i):
if h.heap[g] > item:
return False
else:
if h.heap[g] < item:
return False
return True
Your algorithm is missing some checks. Consider the example below that is not a min-max heap but passes your test. Consider 5 to be the root. The root has another branch but it is not shown for simplicity.
Using your algorithm, the heap below is declared as a min-max heap, but it does not satisfy min-max heap properties. Your algorithm needs to check the parent node as well.
EDIT: A min-max heap is a binary tree that satisfies two properties:
1) T has the heap-shape
2) T is min-max ordered: values stored at nodes on even (odd) levels are smaller (greater) than or equal to the values stored at their descendants (if any) where the root is at level zero.