Experiment with larger inputs; what can you observe from the number of swaps performed?
With larger inputs, there will be more swaps performed, especially if the inputs are not sorted and are randomized. There will more bubble up and trickle down operations performed. However, if there will be less outputs, like what we have in our sample inputs, we would have less number of swaps and operations. However, it still depends on the “sortedness” of our randomized inputs.
Here, I experimented with a larger input.

Same as in the sample inputs, the first number is how many numbers in that test case. For this, I chose 100 random integers, which is from 1 to 100 in random order.
Here is the output. I’ve got many Bubble Ups and Trickle Downs.

Discuss the benefit of a BinaryHeap over a balanced binary search tree.
First of all, the binary heap will be most of time, a complete binary tree, with the leaf nodes at the far left. And if ever the child node will be bigger than its parent, the values of both will be swapped. There isn’t much movement in binary heap also. What I observed here in binary heap is that we always remove in the root, and we also add the new data in the last available position in the heap. However, the most beneficial trait that binary heap has over a balanced binary search tree is that binary heaps use arrays, rather than keeping track of specific individual nodes with their contents and pointers. In binary heaps, we can access everything in the array / list we made for it. Interestingly enough, we can somewhat traverse and iterate through the parent, and the child nodes with the given formulas. I think that binary heaps are more efficient especially when we have bigger data. Accessing data is fast too, because it will be in O(1) as compared in BST which is on average, O(log n).
Code Used:
class BinaryHeap:
def __init__(self, capacity):
self.storage = [0] * capacity
self.capacity = capacity
self.size = 0
self.trickleDownCount = 0
self.bubbleUpCount = 0
# index helper functions
def getParentIndex(self, index): # gets the parent index
return (index - 1) // 2
def getLeftChildIndex(self, index): # gets the left child index
return (2 * index) + 1
def getRightChildIndex(self, index): # gets the left child index
return (2 * index) + 2
# checker helper functions
def hasParent(self, index): # checks if the node has a parent
return self.getParentIndex(index) >= 0
def hasLeftChild(self, index): # checks if the node has a left child
return self.getLeftChildIndex(index) < self.size
def hasRightChild(self, index): # checks if the node has a right child
return self.getRightChildIndex(index) < self.size
# value helper functions
def parent(self, index): # gets the parent value
return self.storage[self.getParentIndex(index)]
def leftChild(self, index): # gets the left child value
return self.storage[self.getLeftChildIndex(index)]
def rightChild(self, index): # gets the right child value
return self.storage[self.getRightChildIndex(index)]
# other helper functions
def isFull(self): # checks if the heap is full or not
return self.size == self.capacity
def swap(self, index1, index2): # swapper function
temp = self.storage[index1]
self.storage[index1] = self.storage[index2]
self.storage[index2] = temp
# main functions
def add(self, data):
if self.isFull():
raise("Heap is Full")
self.storage[self.size] = data # puts the data in the last available position
self.size += 1
self.bubbleUp()
def bubbleUp(self):
index = self.size - 1 # gets the last element of the heap; the recently added node
while self.hasParent(index) and self.parent(index) > self.storage[index]: # checks if the node has a parent
self.bubbleUpCount += 1
self.swap(self.getParentIndex(index), index) # swaps the recently added node with its parent
index = self.getParentIndex(index) # goes further up in the tree until it terminates in the root
def remove(self):
if self.size == 0:
raise("Empty Heap")
data = self.storage[0] # save the data at the root node
self.storage[0] = self.storage[self.size - 1] # replaces the root node with the last element of the heap
del self.storage[self.size - 1]
self.size -= 1
self.trickleDown()
return data # returns the data from the root of the heap that was removed
def trickleDown(self):
index = 0 # starts at the root node and goes down to the tree
while self.hasLeftChild(index): # while there is a left child (in this case, if there is still a node other than the root),
self.trickleDownCount += 1
smallerChildIndex = self.getLeftChildIndex(index)
if self.hasRightChild(index) and (self.rightChild(index) < self.leftChild(index)): # checks if the right child is smaller than the left child of the current node
smallerChildIndex = self.getLeftChildIndex(index)
if self.storage[index] < self.storage[smallerChildIndex]: # checks if the parent is smaller than the its child
break
else:
self.swap(index, smallerChildIndex) # swaps the value of the current index its child if the child is smaller
index = smallerChildIndex # goes down to the tree
input_dat = []
# reading input.dat
with open('input.dat', 'r') as input:
input.readline()
for line in input:
input_dat.append(line.split())
_input_dat = []
for input in input_dat:
_input_dat.append(input[1:])
# evaluating each test case
for i in _input_dat:
heap = BinaryHeap(len(i))
for j in i:
heap.add(int(j))
sorted_contents = heap.storage
# print(sorted_contents)
# sorted_contents = "".join(str(sorted_contents))
_sorted_contents = []
for content in sorted_contents:
_sorted_contents.append(str(content))
while heap.size:
heap.remove()
# print("Bubble Up: %d || Trickle Down: %d" % (heap.bubbleUpCount, heap.trickleDownCount))
# print(_sorted_contents)
# print()
print(f"{heap.bubbleUpCount} {heap.trickleDownCount}", end=" ")
for i in _sorted_contents:
print(i, end=' ')
print()
# For reference, I used this video
# https://www.youtube.com/watch?v=hkyzcLkmoBY&t=1545s
Sample Input
3
10 172 1 130 436 200 155 484 77 147 302
20 138 252 190 225 155 403 408 124 196 322 237 270 351 215 409 57 346 328 431 305
30 251 380 86 177 381 322 74 365 175 373 182 108 210 429 410 242 131 415 255 352 13 491 374 429 310 209 394 173 441 212
Sample Output
4 13 1 77 130 147 200 155 484 436 172 302
12 36 57 124 190 138 225 270 215 155 196 305 237 403 351 408 409 252 346 328 431 322
25 47 13 74 86 175 131 108 173 177 255 182 373 310 209 251 212 380 242 415 365 381 352 491 374 429 322 210 394 429 441 410