I'm coding a O(n) algorithm of 'heapifying' a list in Python. I can't understand why it's not working.
def func(l):
size=len(l)
for root in range((size//2)-1,-1,-1):
child = 2*root+1 #parent of child is target
while(child<size):
#l[child] should be smaller sibling
if child<size-1 and l[child]>l[child+1]:
child+=1
#can we put l[root] in l[child//2]?
if l[root]<=l[child]:
break #yes
#no
l[child//2]=l[child]#move child up
child=2*child+1#move down a level
l[child//2]=l[root]
return l
There are two issues with your function.
The first is quite simple to grasp. You're using the wrong calculation to find the parent of your child index. Rather than child // 2, you should be using (child - 1) // 2. This was causing you to shift some values into the wrong spots.
The second issue is a bit more subtle. If l[root] is larger than one of its children, you're currently overwriting it with that child, and so when you try to insert it in another place later in the list, the original value is no longer available. Probably you should save l[root] at the top of the for loop, then use the saved value any time you're currently examining l[root] later in the code (the if inside the while loop and the final assignment after it ends.
Here's a fixed version of the code, with comments to point out the changes I made:
def func(l):
size=len(l)
for root in range((size//2)-1,-1,-1):
root_val = l[root] # save root value
child = 2*root+1
while(child<size):
if child<size-1 and l[child]>l[child+1]:
child+=1
if root_val<=l[child]: # compare against saved root value
break
l[(child-1)//2]=l[child] # find child's parent's index correctly
child=2*child+1
l[(child-1)//2]=root_val # here too, and assign saved root value
return l
Nice explanation above, here is a little revised version:
# heapify in place
def heapify(A):
# write your code here
for root in xrange(len(A)//2 - 1, -1, -1):
rootVal = A[root]
child = 2 * root + 1
while child < len(A):
if child+1 < len(A) and A[child] > A[child + 1]:
child += 1
if rootVal <= A[child]:
break
A[child], A[(child - 1)//2] = A[(child - 1)//2], A[child]
child = child * 2 + 1
Related
I am finding count of all the ways a target is reached. In base case, i am updating the value but when returning, it is taking the initial value only. How to change it to updated value, Kindly help me making changes in this code only and let me know how can i store so that it can return the modified value.
Input list:[1,2,3]
target:3
Output: 2 as [1,2] and [3] will make it 3
def counter(ind,grid,target,count):
if target==0: #if target becomes 0(achieved)
count+=1
return count
if ind==0: #if ind=0 is reached and target=value at that index(achieved)
if target==grid[ind]:
count+=1
return count
else:
return
nottake=counter(ind-1,grid,target,count) #not taking the index's value
take=0
if target-grid[ind]>=0: #only if value at index is smaller that target
take=counter(ind-1,grid,target-grid[ind],count) #taking the index's value
return count
grid=[1,2,3]
target=3
ind=len(grid)-1
print(counter(ind,grid,target,0)) #output should be 2 but i am getting 0
For starters, please format your code with Black. It's difficult to understand code that's scrunched together. Also, use default values to avoid burdening the caller with fussy indices and extra parameters.
This approach exhibits a common mistake with recursion: trying to pass the result downward through the recursive calls as well as upward. Just pass the result upward only, and pass parameters/state downward.
Doing so gives:
from functools import cache
#cache
def count_ways(available_numbers, target, i=0):
if target == 0:
return 1
elif target < 0:
return 0
elif i >= len(available_numbers):
return 0
take = count_ways(available_numbers, target - available_numbers[i], i + 1)
dont_take = count_ways(available_numbers, target, i + 1)
return take + dont_take
if __name__ == "__main__":
print(count_ways(available_numbers=(1, 2, 2, 1, 3, 4) * 70, target=7))
This is clearly exponential since each recursive call spawns 2 child calls. But adding a cache (formerly lru_cache(maxsize=None) prior to CPython 3.9) avoids repeated calls, giving a linear time complexity as long as the list fits within the stack size. Use a bottom-up dynamic programming approach if it doesn't
I'm trying to figure out how I can initialize a min heap using an array. So far my function looks like this:
def start_heap(self,n):
# None positions to be filled with Nodes
arr = [none][] * n
for i in arr:
heapify(i)
def heapify(self):
start = self.parent(len(self) - 1) # Start at parent of last leaf
for j in range(start, -1, -1): # going to and including the root.
self.heapify_down(j)
def heapify_down(self, i):
n = len(self._pq)
left, right = self.left(i), self.right(i)
if left < n:
child = left
if right < n and self.pq[right] < self.pq[left]:
child = right
if self.pq[child] < self.pq[i]:
self.swap(i, child)
self.heapify_down(child)
Heapify Down Pseudocode:
Heapify-down(H,i): Let n = length(H) If 2i>n then
Terminate with H unchanged Else if 2i<n then
Let left=2i, and right=2i+1
Let j be the index that minimizes key[H[left]] and key[H[right]] Else if 2i=n then
Let j=2i Endif
If key[H[j]] < key[H[i]] then
swap the array entries H[i] and H[j] Heapify-down(H , j)
Endif
I'm going to build a simple node class that just holds data but I'm not sure how to actually get the start_heap function working. Keep in mind n is the maximum number of elements that can be stored.
Some remarks on the code you provided (not on the code you didn't provide):
arr is a local variable, so whatever you do with it, once the function returns, that arr will be out of scope... and lost. You need an attribute or else subclass list
It is not common practice to "allocate" the array and fill it with None. In Python lists are dynamic, so you don't need to reserve slots ahead of time. You just need an empty list to start with.
There is no need to call heapify when the heap is empty.
There is certainly no need to call heapify many times in a loop. All the logic for heapifying is already present in that method, so no need to call it on each index individually. But as stated in the previous point: no need to call it on an empty list -- there is nothing to move.
So the correction is quite basic:
def start_heap(self, max_size):
self.max_size = max_size
self._pq = []
Then, in many of your other methods, you will have to work with self._pq and self.max_size.
For instance, it could have a method that indicates whether the heap is full:
def is_full(self):
return len(self._pq) >= self.max_size
If you have an add method, it would first check if there is still room:
def add(self, node):
if is_full(self):
raise ValueError("Cannot add value to the heap: it is full")
# ... rest of your code ...
I have a problem with the recursion. The function I wrote should recursively generate and return a list of pairs, called chain. The breaking condition is when the pair, (remainder, quotient) already belongs to the chain-list, then stop iterating and return the list. Instead of completing, the recursion just blows up, raising a RecursionError. The list doesn't update and contains only a single term, so the breaking condition is not executed. I don't understand why...
How should I proper implement the recursive step to make the list update?
def proper_long_division(a, b):
"""a < b"""
chain = []
block_size = len(str(b)) - len(str(a))
a_new_str = str(a) + '0' * block_size
a_new = int(a_new_str)
if a_new < b:
a_new = int(a_new_str + '0')
quotient = a_new // b
remainder = a_new - b * quotient
print(remainder)
#print(chain)
# breaking condition <--- !
if (remainder, quotient) in chain:
return chain
# next step
chain.append((remainder, quotient))
chain.extend(proper_long_division(remainder, b))
return chain
try:
a = proper_long_division(78, 91)
print(a)
except RecursionError:
print('boom')
Here a an example of recursion which (should) follows the same structure but the returned list is updated. I don't know why one code works while the other does not.
import random
random.seed(1701)
def recursion():
nrs = []
# breaking condition
if (r := random.random()) > .5:
return nrs
# update
nrs.append(r)
# recursive step
nrs.extend(recursion())
return nrs
a = recursion()
print(a)
# [0.4919374389681155, 0.4654907396198952]
When you enter proper_long_division, the first thing you do is chain = []. That means that the local variable chain refers to a new empty list. Then you do some algebra, which does not affect chain, and check if (remainder, quotient) in chain:. Clearly this will always be False, since chain was and has remained empty.
The next line, chain.append((remainder, quotient)) runs just fine, but remember that only this call to proper_long_division has a reference to it.
Now you call chain.extend(proper_long_division(remainder, b)). You seem to expect that the recursive call will be able to check and modify chain. However, the object referred to by chain in a given call of proper_long_division is only visible within that call.
To fix that, you can use a piece of shared memory that any invocation of the recursive function can see. You could use a global variable, but that would make the function have unpredictable behavior since anyone could modify the list. A better way would be to use a nested function that has access to a list in the enclosing scope:
def proper_long_division(a, b):
"""a < b"""
chain = {}
def nested(a, b):
while a < b:
a *= 10
quotient = a // b
remainder = a - b * quotient
key = (remainder, quotient)
if key in chain:
return chain
# next step
chain[key] = None
nested(remainder, b)
nested(a, b)
return list(chain.keys())
A couple of suggested changes are showcased above. Multiplication by 10 is the same as padding with a zero to the right, so you don't need to play games with strings. Lookup in a hashtable is much faster than a list. Since ordering is important, you can't use a set. Instead, I turned chain into a dict, which is ordered as of python 3.6, and used only the keys for lookup. The values all refer to the singleton None.
The second example does not match the structure of the first in the one way that matters: you do not use nrs as part of your exit criterion.
I have started to learn Recursion , I seem to understand the concept , but at the same time I feel completely lost.
For eg, I was trying to solve find the ancestors of a given node in Binary Tree
My Tree:
1
/ \
7 9
/ \ / \
6 5 2 3
My code :
def findAns(node,target,ans):
if node is None:
return ans
ans.append(node.data)
if node == target:
return ans[:-1] #return the list except the last item(whch will be the target node)
ans = findAns(node.left,target,ans)
ans = findAns(node.right,target,ans)
del ans[-1] #Delete last node while backtracking
return ans
ans=[]
findAns(root,target,ans) #target node is 5
print(ans)
print(ans[:-1])
OUTPUT :
[1,7,5]
[1, 7]
I am unable to understand the below questions,
When backtracking is completed for the entire tree, list 'ans' will be empty # root position, how come I am able to get the value [1,7,5] in my list ?
when the if condition is satisfied , I return ans[:-1] , because I should not include the target node, but how come when I print 'ans' I am getting the target node as well ?
In the same 'if condition' , if I return ans instead of ans[:-1], y code doesn't work, I get back an empty 'ans' list, why is that ?
But to avoid the confusion I changed my code as mentioned below, I used a global variable . However I don't think it is efficient , any resource or material or explanations for my above question would be of great help to me. Thanks !
Code with Global variable:
_ans=[]
def findAns(node,target,ans):
global _ans
if node is None:
return ans
ans.append(node.data)
if node == target:
_ans.append(list(ans[:-1]))
#return ans[:-1]
ans = findAns(node.left,target,ans)
ans = findAns(node.right,target,ans)
del ans[-1]
return ans
ans=[]
findAns(root,target,ans)
_ans[0]
You expected the root after backtracking returns empty list, well it does. The problem is the function returns the empty list but you don't catch it:
res=findAns(root, target, ans)
print(res)
Output:
[]
From question 1 you may still think printing ans will be the same as catching the return value, but when you return ans[:-1] you have already lost the original list you passed in. This is heavily related to list in python being mutable and list[:-1] will return a completely new list and modifying the new list won't affect the ans you first passed in. So after it returns a new list, the backtracking ans = findAns(root.right, target, ans) takes a new list, after that changing the 'new' ans won't change the 'old' ans(the one used to print)
If you change to return ans then it will print out empty list. The problem is every time you iterate through a step in the recursion you append a new node but at the same time you delete one last node, which results in add one and delete one being zero.
I am to implement a length method for a custom Phylogenetic Tree class so we can call len(TreeObject) on it. The length of a tree is defined by how many leafs it has. A leaf means that node has no children. 'self.children' is equal to a list of tuples (node, weight) of that nodes children. I am very close I believe:
def __len__(self):
# everytime it reaches the base case I should add 1
if self.isLeaf():
print('base case - reached leaf!')
return 1
for t,w in self.children:
print('not leaf so sent through loop')
numLeaves = len(t)
return numLeaves
The code is reaching the if statement the correct number of times, e.g. if the length is 3 it outputs 'base case - reached leaf!' 3 separate times. I just need a way of adding those together and storing it in a variable.
Very close indeed. You are just overwriting numLeaves instead of summing them:
numLeaves = 0
for t,w in self.children:
print('not leaf so sent through loop')
numLeaves += len(t)
It can also be implemented differently:
sum(len(t) for (t,w) in self.children)