Python: Heapify k-order min-heap? - python

I'm creating a class for a K-Order Min-heap. I'm storing the heap as a list. I'm having trouble with implementing remove_min. I know that the process of removing the minimum of a heap is:
Remove first element. This is the minimum.
Swap the first element and the last element.
Bubble the new top element down until it satisfies the heap property.
So I need a remove_min and a helper function, bubbledown. I can't use heapq because it only accounts for binary heaps and this class needs to take a k-order heap. Here's what I have so far:
class KHeap:
def __init__(self, lst=[], k=2):
self.heap = []
self.k = k #order of heap
for v in lst:
self.insert(v)
def children(self, i): #returns a list of the children of the item in index i
heap = self.heap
result = []
for x in range(self.k*i+1, self.k*i+self.k+1):
if x<len(heap):
result.append(heap[x])
else:
pass
return result
def parent(self, i): #returns the parent of item in index i
heap = self.heap
if i==0:
return None
result = i//self.k
return heap[result]
def bubbleup(self, i):
if i == 0:
return None
elif self.heap[i] < self.parent(i):
self.heap[i], self.heap[i // self.k] = self.heap[i // self.k], self.heap[i]
self.bubbleup(i // self.k)
def insert(self, value): #use bubbleup
self.heap.append(value)
self.bubbleup(len(self.heap)-1)
def bubbledown(self, i, d=1):
if i==0:
return None
small = i
for k in range(self.children(i)):
if self.heap[k]<self.heap[small]:
small = k
self.heap[i], self.heap[small] = self.heap[small], self.heap[i]
self.bubbledown(small)
def remove_min(self): #use bubbledown
if len(self.heap) == 0:
return None
if len(self.heap) == 1:
return self.heap.pop()
minimum = self.heap[0]
self.heap[0] = self.heap.pop()
self.bubbledown(0)
return minimum
Now, when I remove_min, the result isn't heapified. For example, if I have a ternary heap [1, 10, 18, 22, 15, 30], k=3 and I remove the minimum, the result is [30, 10, 18, 22, 15]. It seems like the element that I move to the top never gets bubbled down.

So I post a iteration version, which can solve the i == 0 problem.
def bubbledown(self, i, d=1):
small = i
size = len(self.heap)
while (i < size):
// find the smallest child
for k in range(self.children(i)):
if self.heap[k] < self.heap[small]:
small = k
self.heap[i], self.heap[small] = self.heap[small], self.heap[i]
// stop here
if small == i:
break
else:
i = small

Related

maximum heap priority queue python

For school i have to make an assignment -->
"The city of Amsterdam wants to store the maximum values of the past few years for research
purposes. It is important that the current maximum measured value can be accessed very quickly.
One idea to fulfill this requirement is to use a priority queue. Your job is to implement a priority
queue with a maximum heap and return again a tuple of the current maximal measurement and
its corresponding date when the maximum occurred. Output: date,covid level"
The program takes as input:
(string)’yyyy-mm-dd’, (int)sensor id, (int)covid level.
The expected output is: yyyy-mm-dd,covid level.
Input: 2022−09−08, 23, 371; 2022−09−08, 2, 3171; 2022−09−08, 12, 43; 2021−03−21, 4, 129
Output: 2022 −09 −08, 3171
I have provided my code below. When creating a max heap, the max element should be the first element (root). A Max-Heap is a complete binary tree in which the value in each internal node is greater than or equal to the values in the children of that node, though when inserting the tuples the nodes do not get sorted. My output is very strange, i do not understand where it comes from. When putting in the above input, this is my output:
1.1.1977, 9223372036854775807
could somebody help me? what piece of code am i missing, i have gone over it so many times.
import sys
class MaxHeap:
def __init__(self, maxsize):
self.maxsize = maxsize
self.size = 0
self.Heap = [0] * (self.maxsize + 1)
self.Heap[0] = ('1.1.1977', sys.maxsize)
self.FRONT = 1
# Function to return the position of
# parent for the node currently
# at pos
def parent(self, pos):
return pos // 2
# Function to return the position of
# the left child for the node currently
# at pos
def leftChild(self, pos):
return 2 * pos
# Function to return the position of
# the right child for the node currently
# at pos
def rightChild(self, pos):
return (2 * pos) + 1
# Function that returns true if the passed
# node is a leaf node
def isLeaf(self, pos):
if pos >= (self.size // 2) and pos <= self.size:
return True
return False
# Function to swap two nodes of the heap
def swap(self, fpos, spos):
self.Heap[fpos], self.Heap[spos] = (self.Heap[spos],
self.Heap[fpos])
# Function to heapify the node at pos
def maxHeapify(self, pos):
if not self.isLeaf(pos):
if (self.Heap[pos] < self.Heap[self.leftChild(pos)] or
self.Heap[pos] < self.Heap[self.rightChild(pos)]):
if (self.Heap[self.leftChild(pos)] >
self.Heap[self.rightChild(pos)]):
self.swap(pos, self.leftChild(pos))
self.maxHeapify(self.leftChild(pos))
else:
self.swap(pos, self.rightChild(pos))
self.maxHeapify(self.rightChild(pos))
# Function to insert a node into the heap
def insert(self, element):
if self.size >= self.maxsize:
return
self.size += 1
self.Heap[self.size] = element
current = self.size
while (self.Heap[current] >
self.Heap[self.parent(current)]):
self.swap(current, self.parent(current))
current = self.parent(current)
# Function to print the contents of the heap
def Print(self):
for i in range(1, (self.size // 2) + 1):
print(i)
print("PARENT : " + str(self.Heap[i]) +
"LEFT CHILD : " + str(self.Heap[2 * i]) +
"RIGHT CHILD : " + str(self.Heap[2 * i + 1]))
# Function to remove and return the maximum
# element from the heap
def extractMax(self):
extraction = self.Heap[self.FRONT]
self.Heap[self.FRONT] = self.Heap[self.size]
self.size -= 1
self.maxHeapify(self.FRONT)
return extraction
# Driver Code
if __name__ == "__main__":
input = input()
input = input.split(";")
dates = []
values = []
for d in input:
date = d.split(',', 2)
dates.append(date[0])
values.append(date[2])
values = [int(x) for x in values]
tuples = list(zip(dates, values))
heap = MaxHeap(len(tuples) + 1)
# print(tuples)
for t in tuples:
heap.insert(t)
print(t)
print(heap.extractMax())

Zigzag level order traversal

I am trying to do the zigzag level order traversal of a binary tree's nodes values (ie, from left to right, then right to left for the next level and alternate between) on https://www.interviewbit.com/problems/zigzag-level-order-traversal-bt/ But the compiler gives time limit exceeded error. How can I resolve it?
# Definition for a binary tree node
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# #param A : root node of tree
# #return a list of list of integers
def zigzagLevelOrder(self, A):
st_crt_lvl =[A]
st_nxt_lvl =[]
ans_list = []
while st_crt_lvl:
u = st_crt_lvl.pop(0)
ans_list.append(u.val)
if u.left:
st_nxt_lvl.append(u.left)
if u.right:
st_nxt_lvl.append(u.right)
while st_nxt_lvl:
u = st_nxt_lvl.pop()
ans_list.append(u.val)
if u.right:
st_crt_lvl.append(u.right)
if u.left:
st_crt_lvl.append(u.left)
return ans_list
You can eliminate multiple inner while loops from your code, by making the queue st_nxt_lvl temporary and copying the content of this temporary queue to the current one st_crt_lvl at the end processing of each level of the binary tree.
This could be achieved by keeping just a single queue (without any temporary storage) and level order traversal by standard bfs algorithm, but since we want zig-zag order traversal at each level, it's more elegant to have a temporary queue, so that the temporary queue only keeps the next level elements and when processing of the current level elements is done, the current queue points to the next level.
With some modification of your code, along with an example tree:
def zigzagLevelOrder(A):
st_crt_lvl = [A] # initialize
ans_list = []
level = 0
while st_crt_lvl: # check if all levels are processed
st_nxt_lvl =[] # temporary queue to hold the next level elements
tmp = [] # temporary storage to append to the ans_list
while st_crt_lvl:
u = st_crt_lvl.pop(0)
tmp.append(u.val)
if u.left:
st_nxt_lvl.append(u.left)
if u.right:
st_nxt_lvl.append(u.right)
if (len(tmp) > 0): # if tmp is not empty
if level % 2 == 1: # ensure zig-zag level order traversal
tmp = tmp[::-1]
ans_list.append(tmp)
st_crt_lvl = st_nxt_lvl # copy the temporary queue to the current queue
level += 1
return ans_list
class BinaryTree:
def __init__(self, left, right, data):
self.left = left
self.right = right
self.val = data
A = BinaryTree(None, None, 3)
A.left = BinaryTree(None, None, 9)
A.right = BinaryTree(None, None, 20)
A.left.left = BinaryTree(None, None, 1)
A.left.right = BinaryTree(None, None, 2)
A.right.left = BinaryTree(None, None, 15)
A.right.right = BinaryTree(None, None, 7)
zigzagLevelOrder(A)
# [[3], [20, 9], [1, 2, 15, 7]]
You can use a breadth-first search:
from collections import deque, defaultdict
class Tree:
def __init__(self, **kwargs):
self.__dict__ = {i:kwargs.get(i) for i in ['left', 'right', 'value']}
def __contains__(self, _val):
if self.value != _val and self.left is None and self.right is None:
return False
return True if self.value == _val else any([_val in [[], self.left][self.left is not None], _val in [[], self.right][self.right is not None]])
def __lookup(self, _val, _depth = 0):
if self.value == _val:
return _depth
return getattr(self, 'left' if _val in self.left else 'right').__lookup(_val, _depth+1)
def __getitem__(self, _val):
return self.__lookup(_val)
def bfs(_head):
_d = deque([_head])
_seen = []
_last = None
_final_result = defaultdict(list)
while _d:
_current = _d.popleft()
if _current.value not in _seen:
_seen.append(_current.value)
_r = list(filter(None, [getattr(_current, i, None) for i in ['left', 'right']]))
_d.extend(_r)
_final_result[_head[_current.value]].append(_current.value)
marker = iter(range(len(_final_result)))
return [i[::-1] if next(marker)%2 else i for _, i in sorted(_final_result.items(), key=lambda x:x[0])]
When constructing the same tree in the example from the link in the question, the output is:
t = Tree(value=3, left=Tree(value=9), right=Tree(value=20, left=Tree(value=15), right=Tree(value=7)))
print(bfs(t))
Output:
[[3], [20, 9], [15, 7]]
This solution uses the breadth-first search to traverse the tree horizontally. However, since a (complete) binary tree has two children, only two values will be added to the queue. Therefore, in order to determine the current tree level the search has reached, a method to find the depth of a particular value has to be implemented in the tree (__getitem__ and __lookup).

Each value in the node of a chain is calculated from the previous value plus the sum of the digits of the previous value

I'm pretty new to this sort of thing and am trying to find how to work out a function which takes two integer values, the starting value and how many nodes in total should be in the chain. Each value in the chain of a node is calculated from the previous value plus the sum of the digits of the previous value. For example:
(409, 5)
would generate a chain of
409
422
430
437
451
My code right now:
class Node:
def __init__(self, init_data):
self.data = init_data
self.next = None
def get_data(self):
return self.data
def get_next(self):
return self.next
def set_data(self, new_data):
self.data = new_data
def set_next(self, new_next):
self.next = new_next
def __str__(self):
return str(self.data)
def generate_chain(start, n):
node = Node(start)
current = start
string_of_nodes = str(start)
list_of_nodes = []
print(current)
for digit in string_of_nodes:
list_of_nodes.append(int(digit))
for i in range(n-1):
node.set_next(sum (int(a) for a in list_of_nodes) + int(current))
current += (node.get_data())
print(current)
Generates an output of:
409
818
1227
1636
2045
2454
I'm wondering if someone can help me find my error and guide me the right way. Thank you.
An alternative implementation that uses the Node class can be the following:
def generate_chain(start, n):
head = node = Node(start)
for _ in range(n):
sum_of_digits = 0
v = node.get_data()
while v > 0:
sum_of_digits += v % 10
v //= 10
node.set_next(Node(node.get_data() + sum_of_digits))
node = node.get_next()
node = head
while node:
print(node.get_data())
node = node.get_next()
This code creates a chain of nodes and then prints. The issue in the original code seems to be that the chain was not constructed correctly since set_next was called with int instead of Node.
This could be implemented nicely as a generator I suppose:
def chain(steps, value):
for i in range(steps):
yield value
value+=sum([int(i) for i in str(value)])
x = chain(5, 409)
for i in x:
print(i)
If you want to use the Node class, you can use this function:
def generate_chain(start, n):
node = Node(start)
for k in range(n):
yield node.get_data()
node.set_next(node.get_data() + sum(int(i) for i in str(node)))
node.set_data(node.get_next())
value = node.get_data()
I = generate_chain(409, 5)
for i in I:
print(i)
Short solution using s.append(x) function and arithmetic modulo operator %:
def generate_chain(num, total):
chain = [num] # setting the initial value
total -= 1
while total:
chain.append(chain[-1] + chain[-1] // 100 + chain[-1] // 10 % 10 + chain[-1] % 10)
total -= 1
return chain
print(generate_chain(409, 5))
The output:
[409, 422, 430, 437, 451]

Linked List Operations

Hello I need help trying to figure out these three functions. I am very new to python.
Assignment:
createList(srcSeq) - creates a linked list from the values
contained in the srcSeq sequence structure and returns the head
reference of the new linked list. The values will be added one at a
time by prepending them to the linked list. myValues = [5, 12, 82,
32, 20] myList = createList(myValues)
size(theList) - given the
head reference (theList), returns the number of elements in the
linked list.
printList(theList) - given the head reference
(theList), prints the values in the linked list from front to back
all on one line with the values separated by commas.
valueAt(theList, index) - returns the value contained in the node at
the given index position where the first value is at position 0, the
second at position 1 and so on. If index is out of range, return
None.
append(theList, value) - appends a new value to the end of
the linked list. Assume the list contains at least one node.
concat(listA, listB) - joins or concatenates two linked lists by
linking the last node of listA to the first node of listB.
split(theList) - given the head reference (theList), splits the
linked list in half to create two smaller linked lists. The head
reference of the linked list created from the second half of the list
is returned. Assume the list contains at least one node. If there is
an odd number of nodes in the linked list, the extra node can be
placed in either of the two new lists.
For the append, concat, do I simply just do. I do not know how to do the split method:
def append (theList, value):
current = self.head
while current.self.next != None:
current = self.next
current.newnode
def concat(listA, listB):
if listA.tail == None:
listA.head = listB.head
else:
listA.tail.next = listB.head
elif listB.head != None:
listA.tail = listB.tail
My Entire Code:
def createList( self ):
self.head = None
temp = ListNode( value )
self.next = newnext
temp.self.next(self.head)
self.head = temp
return self.head
def size( theList ):
current = self.head
count = 0
while current != None:
count = count + 1
current = current.self.next
return count
def printList( theList ):
node = self.head
while node:
print self.value
node = self.next
def valueAt( theList, index ):
current = head
count = 0
while current != None:
if count == index:
return current
def append( theList, value ):
current = self.head
while current.self.next != None:
current = self.next
current.newnode
def concat( listA, listB ):
if listA.tail == None:
listA.head = listB.head
else:
listA.tail.next = listB.head
elif listB.head != None:
listA.tail = listB.tail
def split( theList ):
pass
I think you problem is under-specified. but with what we have :
Splitting a singly linked list:
def split( head ):
middle = head
current = head
index = 0
while current.next != None:
if index % 2:
middle = middle.next
current = current.next
index += 1
result = middle.next
middle.next = None
return result
But to be honest, there is a lot more wrong with what you have so far.
If those lists were Python lists the solution would be really simple:
def split(a):
return a[:len(a)/2], a[len(a)/2:]
And now some explanation :) :
The function returns a tuple of two lists, where each list is one half of the supplied list a.
What I use above is called slicing and you can think of the colon character as of the word until. You can supply two _arguments beginning and end separated by that semicolon.
Example time!
a = [1,2,3,4,5]
a[:2] == [1,2]
a[2:] == [3,4,5]
a[1:3] == [2,3,4]
a[2,-2] == [3]
a[-3,-2] == [3,4]
Isn't slicing great? And it comes for free! One extra trick, if you want to make a copy of a list you can do that with slicing too!
b = a[:]
Boom, done! :)
There is more to slicing, you can have two colons, but that's a story for another time.
PS:
Out of curiosity I did your homework :)
class Node:
def __init__(self, data):
self.data = data
self.next = None
def __str__(self, *args, **kwargs):
return str(self.data)
def create_list(iterable):
next_node = current_node = None
for item in iterable:
current_node = Node(item)
current_node.next = next_node
next_node = current_node
return current_node
def size(head):
count = 0
while head:
head = head.next
count += 1
return count
def print_list(head):
while head:
print(head, end="")
if head.next:
print(" > ", end="")
head = head.next
print(flush=True)
pass
def value_at(head, index):
while (head):
if index < 1:
return head
index -= 1
head = head.next
return None
def append(head, value):
while head:
if not head.next:
head.next = Node(value)
return
head = head.next
def concat(headA, headB):
while headA:
if not headA.next:
headA.next = headB
return
headA = headA.next
def split(head):
center = head
index = 0
while head:
if index % 2:
center = center.next
head = head.next
index += 1
headB = center.next
center.next = None
return headB
def main():
a = create_list([1, 2, 3, 4, 5, 6, 7, 8, 9])
print("Print list::")
print_list(a)
print("\nSize:")
print(size(a))
print("\nValue at:")
print("a[-1]: %d" % value_at(a, -1).data)
print("a[0]: %d" % value_at(a, 0).data)
print("a[1]: %d" % value_at(a, 1).data)
print("a[5]: %d" % value_at(a, 5).data)
print("a[8]: %d" % value_at(a, 8).data)
# print("value # 9 %d"% value_at(my_head,9).data)
print("\nAppend (10):")
print_list(a)
append(a, 10)
print_list(a)
print("\nConcat a, b:")
print_list(a)
b = create_list([11, 12, 13])
print_list(b)
concat(a, b)
print_list(a)
print("\nSplit:")
print_list(a)
print("..into..")
b = split(a)
print_list(a)
print("Size a: %d" % size(a))
print_list(b)
print("Size b: %d" % size(b))
if __name__ == "__main__":
main()

list with infinite elments

I need to operate on two separate infinite list of numbers, but could not find a way to generate, store and operate on it in python.
Can any one please suggest me a way to handle infinite Arithmetic Progession or any series and how to operate on them considering the fact the minimal use of memory and time.
Thanks every one for their suggestions in advance.
You are looking for a python generator instead:
def infinitenumbers():
count = 0
while True:
yield count
count += 1
The itertools package comes with a pre-built count generator.
>>> import itertools
>>> c = itertools.count()
>>> next(c)
0
>>> next(c)
1
>>> for i in itertools.islice(c, 5):
... print i
...
2
3
4
5
6
This is where the iterator comes in. You can't have an infinite list of numbers, but you can have an infinite iterator.
import itertools
arithmetic_progression = itertools.count(start,step) #from the python docs
The docs for Python2 can be found here
I have another python3 solution (read SICP chapter 3.5)
class Stream:
def __init__(self, head, tail):
self.head = head
self.tail = tail
self.memory = None
self.isDone = False
def car(self):
return self.head
def cdr(self):
if self.isDone:
return self.memory
self.memory = self.tail()
self.isDone = True
return self.memory
def __getitem__(self, pullFrom):
if pullFrom < 1 or self.memory == []:
return []
return [self.car()] + self.cdr()[pullFrom - 1]
def __repr__(self):
return "[" + repr(self.car()) + " x " + repr(self.tail) + "]"
def map(self, func):
if self.memory == []:
return []
return Stream(func(self.car()), lambda: Stream.map(self.cdr(), func))
def from_list(lst):
if lst == []:
return []
return Stream(lst[0], lambda:
Stream.from_list(lst[1:]))
def filter(self, pred):
if self.memory == []:
return []
elif pred(self.car()):
return Stream(self.car(), lambda: Stream.filter(self.cdr(), pred))
else:
return self.cdr().filter(pred)
def sieve(self):
return Stream(self.car(), lambda: self.cdr().filter(lambda n: n % self.car() > 0).sieve())
def foreach(self, action, pull = None):
if pull is None:
action(self.car())
self.cdr().foreach(action, pull)
elif pull <= 0:
return
else:
action(self.car())
self.cdr().foreach(action, pull-1)and run:
a = Stream(0, lambda: a.map((lambda x: x + 1)))
print(a[10])
which returns:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .
But streams are lazily evaluated, so:
>>> a = Stream(0, lambda: a.map((lambda x: x + 1)))
>>> print(a)
prints:
[0 x [...]]
To create an object that acts like a "mutable" infinite list, you can overload the __getitem__ and __setitem__ methods in a class:
class infinite_list():
def __init__(self, func):
self.func = func
self.assigned_items = {}
def __getitem__(self, key):
if key in self.assigned_items:
return self.assigned_items[key]
else:
return self.func(key)
def __setitem__(self, key , value):
self.assigned_items[key] = value
Then, you can initialize the "infinite list" with a lambda expression and modify an item in the list:
infinite_thing = infinite_list(lambda a: a*2)
print(infinite_thing[1]) #prints "2"
infinite_thing[1] = infinite_thing[2]
print(infinite_thing[1]) #prints "4"
Similarly, it is possible to create an "infinite dictionary" that provides a default value for each missing key.
Perhaps the natural way to generate an infinite series is using a generator:
def arith(a, d):
while True:
yield a
a += d
This can be used like so:
print list(itertools.islice(arith(10, 2), 100))
My solution is:
from hofs import *
def cons_stream(head,tail):
return [head,tail,False,False]
def stream_cdr(strm):
if strm[2]:
return strm[3]
strm[3] = strm[1]()
strm[2] = True
return strm[3]
def show_stream(stream, num = 10):
if empty(stream):
return []
if num == 0:
return []
return adjoin(stream[0], show_stream(stream_cdr(stream), num - 1))
def add_streams(a , b):
if empty(a):
return b
if empty(b):
return a
return cons_stream(a[0] + b[0] , lambda : add_streams( stream_cdr(a), stream_cdr(b)))
def stream_filter( pred , stream ):
if empty(stream):
return []
if pred(stream[0]):
return cons_stream(stream[0], lambda : stream_filter(pred, stream_cdr(stream)))
else:
return stream_filter( pred , stream_cdr( stream ))
def sieve(stream):
return cons_stream(stream[0] , lambda : sieve(stream_filter(lambda x : x % stream[0] > 0 , stream_cdr(stream))))
ones = cons_stream(1, lambda : ones)
integers = cons_stream(1, lambda : add_streams(ones, integers))
primes = sieve(stream_cdr(integers))
print(show_stream(primes))
Copy the Python code above.
When I tried it, i got [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] which is 10 of an infinite list of primes.
You need hofs.py to be
def empty(data):
return data == []
def adjoin(value,data):
result = [value]
result.extend(data)
return result
def map(func, data):
if empty(data):
return []
else:
return adjoin(func(data[0]), map(func, data[1:]))
def keep(pred, data):
if empty(data):
return []
elif pred(data[0]):
return adjoin( data[0] , keep(pred, data[1:]))
else:
return keep(pred, data[1:])
I assume you want a list of infinite numbers within a range. I have a similar problem, and here is my solution:
c = 0
step = 0.0001 # the difference between the numbers
limit = 100 # The upper limit
myInfList = []
while c <= limit:
myInfList.append(c)
c = c + step
print(myInfList)

Categories