I am trying to turn a Binary Max Heap into a Ternary Max Heap in Python. My code needs to remain in the same structure as the binary heap below. I need some help figuring out what changes to make. I know the max child method needs to be updated with 3 * i - 1, 3 * i, and 3 * i + 1. I don't know where to begin. Any suggestions would be appreciated.
class BinaryMaxHeap:
def __init__(self):
'''
heap_list[0] = 0 is a dummy value (not used)
'''
self.heap_list = [0]
self.size = 0
def __str__(self):
'''
returns the string representation of the object
'''
return str(self.heap_list)
def __len__(self):
'''
returns a positive integer that represents the length of the object
'''
return self.size
def __contains__(self, item):
'''
instance method, returns boolean value
'''
return item in self.heap_list
def is_empty(self):
'''
compare the size attribute to 0
'''
return self.size == 0
def find_max(self):
'''
the largest item is at the root node (index 1)
'''
if self.size > 0:
max_val = self.heap_list[1]
return max_val
return None
def insert(self, item):
'''
append the item to the end of the list (maintains complete tree property)
violates the heap order property
call percolate up to move the new item up to restore the heap order property
'''
self.heap_list.append(item)
self.size += 1
self.percolate_up(self.size)
def del_max(self):
'''
max item in the tree is at the root
replace the root with the last item in the list (maintains complete tree property)
violates the heap order property
call percolate down to move the new root down to restore the heap property
'''
max_val = self.heap_list[1]
self.heap_list[1] = self.heap_list[self.size]
self.heap_list.pop()
self.size -= 1
self.percolate_down(1)
return max_val
def max_child(self, index):
'''
return the index of the largest child
if there is no right child, return the left child
if there are two children, return the largest of the two
'''
if index*2+1 > self.size:
return index * 2
else:
if self.heap_list[index*2] < self.heap_list[index*2+1]:
return index * 2
else:
return index*2+1
def build_heap(self, alist):
'''
build a heap from a list of keys to establish complete tree property
starting with the first non leaf node
percolate each node down to establish heap order property
'''
index = len(alist)//2 # any nodes past the halfway point are leaves
self.size = len(alist)
self.heap_list = [0] + alist[:]
while (index>0):
self.percolate_down(index)
index -= 1
def percolate_up(self, index):
'''
compare the item at index with its parent
if the item is greater than its parent, swap!
continue comparing until we hit the top of tree
(can stop once an item is swapped into a position where it is greater than its parent)
'''
while index // 2 > 0:
if self.heap_list[index] > self.heap_list[index//2]:
temp = self.heap_list[index//2]
self.heap_list[index//2] = self.heap_list[index]
self.heap_list[index] = temp
index //= 2
def percolate_down(self, index):
'''
compare the item at index with its largest child
if the item is less than its greatestest child, swap!
continue continue while there are children to compare with
(can stop once an item is swapped into a position where it is less than both children)
'''
while (index * 2) <= self.size:
mc = self.max_child(index)
if self.heap_list[index] < self.heap_list[mc]:
temp = self.heap_list[index]
self.heap_list[index] = self.heap_list[mc]
self.heap_list[mc] = temp
index = mc
While a dummy entry can still be interesting for a binary heap implementation, to get the same "benefit" for a ternary heap, you would need 2 dummies. It is better to just adapt the index calculations and have no dummy needs. Without the dummy it becomes also overkill to have a size
attribute, since it always corresponds to the length of the list.
The implementation for a ternary tree just needs to adapt all those occurrences where a coefficient of 2 is used and adapt them to use 3 (and the shift to get the index right).
I would not have created a separate build_heap
method, and certainly not as an instance method, but since you indicated you want the "same structure", I left it like that.
class TernaryMaxHeap:
def __init__(self):
self.heap_list = [] # No more dummy
# No more size attribute. A list knows its size.
def __str__(self):
return str(self.heap_list)
def __len__(self):
return len(self.heap_list)
def __contains__(self, item):
return item in self.heap_list
def is_empty(self):
return not self.heap_list
def find_max(self):
if self.heap_list:
return self.heap_list[0] # No more dummy
def insert(self, item):
self.heap_list.append(item)
self.percolate_up(len(self)-1)
def del_max(self):
max_val = self.heap_list[0]
self.heap_list[0] = self.heap_list[-1]
self.heap_list.pop()
if self.heap_list:
self.percolate_down(0)
return max_val
def max_child(self, index):
child = index * 3 + 1
if child >= len(self) - 1:
return child
# Generic with range, using coefficient 3
return max((self.heap_list[child], child) for child in range(child, min(child + 3, len(self))))[1]
# NB: This alist argument should have better been integrated with the constructor:
def build_heap(self, alist):
self.heap_list = alist[:] # No dummy
for index in range((len(alist) - 2)//3, -1, -1): # Use divisor 3, and pythonic looping
self.percolate_down(index)
def percolate_up(self, index):
val = self.heap_list[index]
parent = (index - 1) // 3 # Coefficient 3, without dummy
while index and self.heap_list[parent] < val:
self.heap_list[index] = self.heap_list[parent]
index = parent
parent = (index - 1) // 3
self.heap_list[index] = val
def percolate_down(self, index):
val = self.heap_list[index]
child = self.max_child(index)
while child < len(self) and val < self.heap_list[child]:
self.heap_list[index] = self.heap_list[child]
index = child
child = self.max_child(index)
self.heap_list[index] = val