Ternary Max Heap Python

Question:

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
Asked By: spicywine123

||

Answers:

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
Answered By: trincot
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.