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
Related
I am just experimenting with a hybrid model of Linked List with some modifications. I have already implemented object.delete_node(index) which just link the next node as it is in vanilla Linked Lists. Now, and want to implement del object[index] which does the same function as object.delete_node(index). How could I implement it? It is implemented in list and dict in Python. Which method is responsible for the same?
Below is the code for my LinkedList which works pretty well.
class Node:
def __init__(self, data = None, next_pointer = None):
self.data = data
self.next_pointer = next_pointer
def __str__(self):
return self.__repr__()
def __repr__(self):
return str(self.data)
class LinkedList:
def __init__(self):
self.head = Node()
self.length = 0
def insert_node(self, data):
new_node = Node(data) # node to be inserted
current_node = self.head
while current_node.next_pointer != None: # it'll only stop at the last node which is obviously empty
current_node = current_node.next_pointer # bring out next pointer
current_node.next_pointer = new_node
self.length += 1
def delete_node(self, index):
if self.length == 0: raise ValueError(f"Can not delete from empty Linked List")
if (index > self.length - 1) or (index < -self.length -1): raise ValueError(f"index {index} out of bounds of max length")
if index < 0: index = self.length + index
count = 0
current_node = self.head
while count < index:
current_node = current_node.next_pointer
count += 1
current_node.next_pointer = current_node.next_pointer.next_pointer if current_node.next_pointer.next_pointer != None else None
self.length -= 1
def _slice_return(self, slice_index):
'''
Implement slicing Operation just like in Python Lists and Strings
'''
index = slice_index.start
stop = min(slice_index.stop, self.length -1)
step = 1 if slice_index.step == None else slice_index.step
if index < 0: raise NotImplementedError("Negative slicing not implemented")
if (index > self.length - 1) or (index < -self.length -1): raise ValueError(f"index {index} out of bounds of max length")
if index < 0: index = self.length + index
ll = LinkedList()
for i in range(index, stop,step):
ll.insert_node(self[i].data)
return ll
def __getitem__(self, index):
if isinstance(index, slice):
return self._slice_return(index)
if (index > self.length - 1) or (index < -self.length -1): raise ValueError(f"index {index} out of bounds of max length")
if index < 0: index = self.length + index
count = 0
current_node = self.head.next_pointer
while count != index:
current_node = current_node.next_pointer
count += 1
return current_node
def __len__(self):
return self.length
def __str__(self):
array = []
node = self.head
count = self.length
while count > 0:
node = node.next_pointer
array.append(node.data)
count -= 1
return(str(array))
def __repr__(self):
return self.__str__()
ll = LinkedList()
ll.insert_node("a")
ll.insert_node("b")
ll.insert_node("A")
ll.insert_node("B")
ll.delete_node(2) # delete 3rd node
Answer based on #jonrsharpe comment:
There is a Datamodel section in python docs with list of available dunder methods
In your case its:
object.__delitem__(self, key)
Called to implement deletion of self[key]. Same note as for __getitem__(). This should only be implemented for mappings if the objects support removal of keys, or for sequences if elements can be removed from the sequence. The same exceptions should be raised for improper key values as for the __getitem__() method.
I am trying to implement the insert method of a circular array-based queue, however am unable to update the rear of the queue. Here is my code:
def __init__(self, max_size):
"""
-------------------------------------------------------
Initializes an empty queue. Data is stored in a fixed-size list.
Use: cq = Queue(max_size)
-------------------------------------------------------
Parameters:
max_size - maximum size of the queue (int > 0)
Returns:
a new Queue object (Queue)
-------------------------------------------------------
"""
assert max_size > 0, "Queue size must be > 0"
self._max_size = max_size
self._values = [None] * self._max_size
self._front = 0
self._rear = 0
self._count = 0
def insert(self, value):
'''-------------------------------------------------------
Adds a copy of value to the rear of the queue.
Use: cq.insert( value )
-------------------------------------------------------
Parameters:
value - a data element (?)
Returns:
None
-------------------------------------------------------'''
assert (self._count < self._max_size), 'queue is full'
self._values.append(deepcopy(value))
self._count += 1
self._rear = (self._rear - 1) % self._count
return
Any suggestions?
edit:
here is the remove implementation:
def remove(self):
'''-------------------------------------------------------
Removes and returns value from the queue.
Use: v = cq.remove()
-------------------------------------------------------
Returns:
value - the value at the front of the queue - the value is
removed from the queue (?)
-------------------------------------------------------'''
assert (self._count > 0), 'Cannot remove from an empty queue'
value = self._values[self._front]
self._front = (self._front + 1) % self._count
self._count += -1
return value
When you add items by appending, you are extending your list beyond the max length that you pre-allocated it to. You then update self._rear as if you were going to use it as the insert index, but never actually use it for anything. I have implemented your code with only very minor changes beyond variable names (in order to make more sense to me), and utilizing self._rear (now: self._write_cursor) in the way I believe you intended.
class CQ: #Circular Queue
def __init__(self, maxsize):
self._maxsize = maxsize
self._write_cursor = 0
self._read_cursor = 0
self._len = 0
self._values = [None] * maxsize
def insert(self, item):
if self._len < self._maxsize:
self._values[self._write_cursor] = item
self._write_cursor = (self._write_cursor + 1) % self._maxsize
self._len = self._len + 1
else:
raise IndexError('can\'t push to full queue')
def remove(self):
if self._len > 0:
out = self._values[self._read_cursor]
self._read_cursor = (self._read_cursor + 1) % self._maxsize
self._len -= 1
return out
else:
raise IndexError('can\'t pop from empty queue')
I need to implement this insert function in my doubly linked list and I'm having trouble getting it to properly insert elements at given indexes. I am able to add an element into an empty list object, but when I attempt to add a new node at the last node, I get an error saying:
'NoneType' object has no attribute 'setPrev'
I understand what this error means and have tried shifting my function around to avoid this error and get the right output, but to no avail.
Question: How can I fix this insert function in order to allow it to add nodes in all cases?
class DLLNode:
def __init__(self,initdata):
self.data = initdata
self.next = None
self.prev = None
def __str__(self):
return str(self.data)
def getData(self):
return self.data
def getNext(self):
return self.next
def getPrev(self):
return self.prev
def setData(self, new_data):
self.data = new_data
def setNext(self, new_next):
self.next = new_next
def setPrev(self, new_prev):
self.prev = new_prev
class DLL:
""" Class representing a doubly-linked list. """
def __init__(self):
""" Constructs an empty doubly-linked list. """
self.head = None
self.size = 0
def __str__(self):
""" Converts the list into a string representation. """
current = self.head
rep = ""
while current != None:
rep += str(current) + " "
current = current.getNext()
return rep
def isEmpty(self):
""" Checks if the doubly-linked list is empty. """
return self.size <= 0
def insert(self, item, index):
""" Inserts a node at the specified index. """
# Construct node.
current = self.head
n = DLLNode(item)
# Check index bounds.
if index > self.size:
return 'index out of range'
# If the list is empty...
if self.isEmpty():
self.head = n
self.head.setPrev(self.head)
# If the index is the first node...
if index == 0:
n.setNext(self.head)
self.head = n
if self.size == 0:
self.prev = n
# If the index is the last node...
elif index == self.size:
n.next.setPrev(n)
# If the index is any other node...
else:
if current == None:
n.setPrev(self.prev)
self.prev.setNext(n)
self.prev = n
else:
n.setNext(current)
n.getPrev().setNext(n)
current.setPrev(n.getPrev())
n.setPrev(n)
self.size += 1
A testcase is the following scenario:
l = DLL()
l.insert(88, 0)
l.insert(99, 1)
l.insert(77, 2)
l.insert(55, 3)
l.insert(34, 1)
l.insert(3, 0)
l.insert(15, 6)
l.insert(100, 8)
print("list after inserts", l)
output is as follows:
Index out of range.
list after inserts 3 88 34 99 77 55 15 """
The problem is that n is a DLLNode you construct yourself. By default prev and next are set to Null; therefore you cannot call any methods on them.
def insert(self, item, index):
""" Inserts a node at the specified index. """
# Construct node.
current = self.head
n = DLLNode(item)
# Check index bounds.
if index > self.size:
return 'index out of range'
# If the list is empty...
if self.isEmpty():
self.head = n
self.head.setPrev(self.head)
else : #added else case to prevent overlap
for x in range(0,index-1): #Obtain the current
current = current.next #move to the next item
# If the index is the first node...
if index == 0:
n.setNext(self.head)
self.head = n
if self.size == 0:
self.prev = n
# If the index is the last node...
elif index == self.size:
current.setNext(n) #set n to be the next of current
n.setPrev(current) #set current to be the previous of n
# If the index is any other node...
else:
n.setNext(current.next)
n.setPrev(current)
if current.next != None :
current.next.setPrev(n)
current.setNext(n)
self.size += 1
The last situation works as follows:
/------\|
C N X
|\------/
with C the currentXthenextofcurrentandNthen(new node). First we set theprevandnextofn`:
/------\|
C <--N-->X
|\------/
Now we check whether X actually is a real node (although this is strictly not necessary, since "last nodes" are handled above). If X is not None, we set the prev of X to N:
/------\|
C <--N-->X
|\-/
Finally we do not longer need C to point to X (otherwise we could not call functions of X), so we set the next of C to N:
/--\|
C <--N-->X
|\-/
Can you provide test data to test if the implementation works correctly?
I believe the problem is here
elif index == self.size:
n.next.setPrev(n)
When insert at the last element, you need to traverse to the current last element say last. Assume you did that you can do
elif index == self.size:
last.setNext(n)
n.setPrev(last)
n.setNext(head) #only if this list is also circular
self.size++
I need to add an index and a count function for my program but I am really confused on how to go about this because my teacher is very broad. Any ideas that I could do for adding an index and a count function into my Linked List?
Here's my code:
from ListNode import ListNode
class LinkedList(object):
#--------------------------------------------------------------
def __init__(self, seq=()):
""" Pre: Creates a Linked List
Post: Creates a list containing the items in the seq=()"""
if seq == ():
# If there is no items to be put into the list, then it creates an empty one.
self.head = None
else:
# Creates a node for the first item.
self.head = ListNode(seq[0], None)
# If there are remaining items, then they're added while keeping track of the last node.
last = self.head
for item in seq[1:]:
last.link = ListNode(item, None)
last = last.link
self.size = len(seq)
#-------------------------------------------------------------
def __len__(self):
'''Pre: Nothing.
Post: Returns the number of items in the list.'''
return self.size
#-------------------------------------------------------------
def _find(self, position):
'''This is a private method, which means it only works in this class.
This returns the last node used. (0 is the first item, size-1 is the last item)
Pre: 0 (less than or equal to) position (less than) self.size
Post: Returns the ListNode at the specified position in the list.'''
assert 0 <= position < self.size
node = self.head
# move forward until we reach the specified node
for i in range(position):
node = node.link
return node
#-------------------------------------------------------------
def append(self,x):
'''This adds (Appends) 'x' onto the end of the list
Post: X is appended to the end of the list.'''
# First create a new node containing x
newNode = ListNode(x)
# This will link it onto the end of the list.
if self.head is not None:
# Not an empty list
node = self._find(self.size - 1)
node.link = newNode
else:
# If it is an empty list.
# You will set self.head to the new node
self.head = newNode
self.size += 1
#-------------------------------------------------------------
def __getitem__(self, position):
''' returns the data item at the location position
Pre: 0 <= position < size
Post: Returns data item at the specified position.'''
node = self._find(position)
return node.item
#-------------------------------------------------------------
def __setitem__(self, position, value):
''' Sets the data item at the location position to the value.
Pre: 0 <= position < self.size
Post: Sets the data item at the specified position to value.'''
node = self._find(position)
node.item = value
#--------------------------------------------------------------
def __delitem__(self, position):
''' Deletes the item at the location position from the list.
Pre: 0 <= position < self.size
Post: The item at the specified position is removed from the list.'''
assert 0 <= position < self.size
self._delete(position)
#--------------------------------------------------------------
def __max__(self):
''' Goes through each node and compares what the max is for the linked list.
Post: Finds the max of the linked list and returns that value.'''
max_value = self.head.item
node = self.head.next
while node is not None:
if node.item > max_value:
max_value = node.item
node = node.link
return max_value
#--------------------------------------------------------------
def __min__(self):
''' Goes through each node and compares what the min is for the linked list.
Post: Finds the min of the linked list and returns that value.'''
min_value = self.head.item
node = self.head.next
while node is not None:
if node.item < min_value:
min_value = node.item
node = node.link
return min_value
#--------------------------------------------------------------
def _delete(self, position):
''' This is a private function where it deletes an item at the location
position from the list.
Pre: 0 <= position < self.size
Post: The item at the specified location is removed from the list.
The item is then returned ( To be used with pop.)'''
if position == 0:
# Save the item from the initial node
item = self.head.item
# Change the self.head to point "over" the deleted node.
self.head = self.head.link
else:
# Find the node before the one that you are deleting.
prev_node = self._find(position - 1)
# Save the item from the node you are deleting.
item = prev_node.link.item
# Change the previous node to go over the deleted node. (Example 2 goes to 4)
prev_node.link = prev_node.link.link
self.size -= 1
return item
#--------------------------------------------------------------
def pop(self, i = None):
''' This returns and removes whatever is at position 'i' from the list. It is
defaulted to return and remove the last item.
Pre: self.size > 0 and ((i is None or (0 <= i < self.size))
Post: If I is None, then the last item in the list is removed and then returned.
Otherwise, the item at position 'i' is removed and returned.'''
assert self.size > 0 and (i is None or (0 <= i < self.size))
# The default is to delete the last item in the list.
# 'i' could be zero so we need to compare to None.
if i is None:
i = self.size - 1
return self._delete(i)
#-----------------------------------------------------------------
def insert(self, i, x):
''' Inserts 'x' at the position 'i' is at in the list.
Pre: 0 <= i <= self.size
Post: x is inserted into the list at position 'i', and the old segment
from position 'i' (oldsize-1) are now at position 'i'+1 (newsize - 1).'''
assert 0 <= i <= self.size
if i == 0:
# Inserting before ) requires changing/updating self.head.
self.head = ListNode(x, self.head)
else:
# Find the item that this node will be inserted after.
node = self._find(x, self.head)
node.link = ListNode(x, node.link)
self.size += 1
#-----------------------------------------------------------------
def __copy__(self):
''' Post: Returns a new Linked List object that is a shallow copy of self.'''
a = LinkedList()
node = self.head
while node is not None:
a.append(node.item)
node = node.link
return a
#-----------------------------------------------------------------
def __iter__(self):
return LinkedListIterator(self.head)
#---------------------------------------------------------------------
class LinkedListIterator(object):
#-----------------------------------------------------------------
def __init__(self, head):
self.currnode = head
#-----------------------------------------------------------------
def next(self):
if self.currnode is None:
raise StopIteration
else:
item = self.currnode.item
self.currnode = self.currnode.link
return item
This may seem basic for you guys but I am struggling to figure out what my teacher wants me to index or count in this program.
The best would be to look at python's documentation of those methods:
[].index.__doc__
[].count.__doc__
My basic idea was to create a linked list, and as each new value comes in, add 1/N times the new value and subtract 1/N times the first value, then move the pointer to first along by one and free the memory that had been associated with first.
This won't ultimately be implemented in Python but just to get the process clear in my head, I tried to write it in Python, but my implementation is flawed. Do I need a doubly linked list for this? Is there an alternative approach (not linked-list based) that would be better?
Here's my attempt so far:
class Link:
def __init__(self,val):
self.next = None
self.value = val
class LinkedList:
def __init__(self,maxlength):
self.current_link = None
self.maxlength = maxlength
self.sum = 0.
self.average = None
self.length = 0
self._first_link = None
def add_link(self,val):
new_link = Link(val)
new_link.next = self.current_link
self.current_link = new_link
if self._first_link is None:
self._first_link = self.current_link
self.sum += val
if self.length < self.maxlength:
self.length += 1
else:
self.sum -= self._first_link.value
self._first_link = self._first_link.next # this line is flawed
self.average = self.sum/self.length
def get_first(self):
return self._first_link.value
# Main
ll = LinkedList(5)
for ii in xrange(10):
ll.add_link(ii)
print ii,ll.get_first(),ll.average
The problem is that _first_link gets set to a value that doesn’t have a next. That is, _first_link gets set to the first item that's added, but its next is None, so I don't see how to move it along by 1 as I want to. This is what makes me wonder if a doubly linked list is needed.
I'd appreciate any advice.
I think the simplest implementation is to use a circular linked list (a.k.a. a ring):
class Link(object):
def __init__(self, value=0.0):
self.next = None
self.value = value
class LinkedRing(object):
def __init__(self, length):
self.sum = 0.0
self.length = length
self.current = Link()
# Initialize all the nodes:
last = self.current
for i in xrange(length-1): # one link is already created
last.next = Link()
last = last.next
last.next = self.current # close the ring
def add_val(self, val):
self.sum -= current.value
self.sum += val
self.current.value = val
self.current = self.current.next
def average(self):
return self.sum / self.length
# Test example:
rolling_sum = LinkedRing(5)
while True:
x = float(raw_input())
rolling_sum.add_val(x)
print(">> Average: %f" % rolling_sum.average())
You can implement this using collections.deque and the numerically stable math for maintaining running averages:
import collections
class AveragingBuffer(object):
def __init__(self, maxlen):
assert( maxlen>1)
self.q=collections.deque(maxlen=maxlen)
self.xbar=0.0
def append(self, x):
if len(self.q)==self.q.maxlen:
# remove first item, update running average
d=self.q.popleft()
self.xbar=self.xbar+(self.xbar-d)/float(len(self.q))
# append new item, update running average
self.q.append(x)
self.xbar=self.xbar+(x-self.xbar)/float(len(self.q))
if __name__=="__main__":
import scipy
ab=AveragingBuffer(10)
for i in xrange(32):
ab.append(scipy.rand())
print ab.xbar, scipy.average(ab.q), len(ab.q)
Okay, I thought of a solution that works in O[1] time. I'm still curious if anyone has a linked-list-based solution, but this solution avoids the LL entirely:
class Recent:
def __init__(self,maxlength):
self.maxlength = maxlength
self.length = 0
self.values = [0 for ii in xrange(maxlength)]
self.index = 0
self.total = 0.
self.average = 0.
def add_val(self,val):
last = self.values[self.index%self.maxlength]
self.values[self.index%self.maxlength] = val
self.total += val
self.total -= last
if self.length < self.maxlength:
self.length += 1
self.average = self.total / self.length
self.index += 1
def print_vals(self):
print ""
for ii in xrange(self.length):
print ii,self.values[ii%self.maxlength]
print "average:",self.average
# Example to show it works
rr = Recent(5)
for ii in xrange(3):
rr.add_val(ii)
rr.print_vals()
for ii in xrange(13):
rr.add_val(ii)
rr.print_vals()