Python - Recursive count through BST - python

so I am doing a Find Kth Largest Value in BST leetcode problem and I want to
update the k_count every time I iterate through the tree. As you can see I print the tree value and the k_count everytime I go through. It works in that I am returning the value of the tree from largest value to smallest but I want the assocaited K_count with each tree node to update respectively. That is I want the largest value to have a k_count of 1 the second largest to have a k_count of 2 the third largest a k_count of 3 etc. I know I am close but getting thrown off with the recursive stack.
Any help with this would be greatly appreciated!
# This is an input class. Do not edit.
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def findKthLargestValueInBst(tree, k):
k_count = 0
result = 0
return findhelper(tree, k, k_count, result)
def findhelper(tree, k, k_count, result):
if tree is not None:
findhelper(tree.right, k, k_count, result)
print(tree.value)
print(k_count)
if k_count == k:
result = tree.value
findhelper(tree.left, k, k_count+1, result)
return result

The main issue in your attempt is that k_count is a local variable that
never is assigned a different value, and
its value is never communicated back to the caller
More concretely, whatever the first recursive call does (to walk through the right subtree), that print(k_count) is always going to print the value of k_count it had before that recursive call was made, simply because that recursive call is not going to affect its value.
Again, each recursive execution of the function will get its own k_count variable, that is not related to any other k_count variable that exists in the call stack.
What you really want, is that when the first recursive call returns to the caller (and has visited the right subtree) that k_count will represent the number of nodes that were visited during that recursive call. But for that you would have to pass a reference to a single k_count attribute somewhere, or have the updated value returned by the recursive function.
But I would suggest to do this with a generator. It makes it so much easier to think about: perform a reversed inorder traversal using yield. Once you have that, just pull the first k values from that iterator and you're done.
Code:
def reversedInOrder(tree):
if tree:
yield from reversedInOrder(tree.right)
yield tree.value
yield from reversedInOrder(tree.left)
def findKthLargestValueInBst(tree, k):
for value in reversedInOrder(tree):
k -= 1
if k == 0:
return value

Related

Root to leaf algo bug

Wrote an unnecessarily complex solution to the following question:
Given a binary tree and a sum, determine if the tree has a
root-to-leaf path such that adding up all the values along the path
equals the given sum.
Anyway, I'm trying to debug what went wrong here. I used a named tuple so that I can track both whether the number has been found and the running sum, but it looks like running sum is never incremented beyond zero. At any given leaf node, though, the running sum will be the leaf node's value, and in the next iteration the running sum should be incremented by the current running sum. Anyone know what's wrong with my "recursive leap of faith" here?
def root_to_leaf(target_sum, tree):
NodeData = collections.namedtuple('NodeData', ['running_sum', 'num_found'])
def root_to_leaf_helper(node, node_data):
if not node:
return NodeData(0, False)
if node_data.num_found:
return NodeData(target_sum, True)
left_check = root_to_leaf_helper(node.left, node_data)
if left_check.num_found:
return NodeData(target_sum, True)
right_check = root_to_leaf_helper(node.right, node_data)
if right_check.num_found:
return NodeData(target_sum, True)
new_running_sum = node.val + node_data.running_sum
return NodeData(new_running_sum, new_running_sum == target_sum)
return root_to_leaf_helper(tree, NodeData(0, False)).num_found
EDIT: I realize this is actually just checking if any path (not ending at leaf) has the correct value, but my question still stands on understanding why running sum isn't properly incremented.
I think you need to think clearly about whether information is flowing down the tree (from root to leaf) or up the tree (from leaf to root). It looks like the node_data argument to root_to_leaf_helper is initialized at the top of the tree, the root, and then passed down through each node via recursive calls. That's fine, but as far as I can tell, it's never changed on the way down the tree. It's just passed along untouched. Therefore the first check, for node_data.num_found, will always be false.
Even worse, since node_data is always the same ((0, False)) on the way down the tree, the following line that tries to add the current node's value to a running sum:
new_running_sum = node.val + node_data.running_sum
will always be adding node.val to 0, since node_data.running_sum is always 0.
Hopefully this is clear, I realize that it's a little difficult to explain.
But trying to think very clearly about information flowing down the tree (in the arguments to recursive calls) vs information flowing up the tree (in the return value from the recursive calls) will make this a little bit more clear.
You can keep a running list of the path as part of the signature of the recursive function/method, and call the function/method on both the right and left nodes using a generator. The generator will enable you to find the paths extending from the starting nodes. For simplicity, I have implemented the solution as a class method:
class Tree:
def __init__(self, *args):
self.__dict__ = dict(zip(['value', 'left', 'right'], args))
def get_sums(self, current = []):
if self.left is None:
yield current + [self.value]
else:
yield from self.left.get_sums(current+[self.value])
if self.right is None:
yield current+[self.value]
else:
yield from self.right.get_sums(current+[self.value])
tree = Tree(4, Tree(10, Tree(4, Tree(7, None, None), Tree(6, None, None)), Tree(12, None, None)), Tree(5, Tree(6, None, Tree(11, None, None)), None))
paths = list(tree.get_sums())
new_paths = [a for i, a in enumerate(paths) if a not in paths[:i]]
final_path = [i for i in paths if sum(i) == 15]
Output:
[[4, 5, 6]]

Linked Lists Homework in Python

I have to do the following for my homework:
Suppose that your linked list is not empty, and is pointed to by the variable head. Write a method called findMin() that goes through the list and finds the least value (you can compare strings just like numbers) and returns that.
Suppose that your linked list is not empty, and is pointed to by the variable head. Write a method called getLast() that goes through the list and returns the last thing in the list. This is the value of the last node in the linked list chain.
Finish the append method. It runs to the end and attaches the new node to the last existing nodes in the chain. If there are no nodes in the chain yet, it sets self.head to that node. Beware! This is tricky! You might consult your textbook.
I attempted to solve the first one and I am just lost, I don't know if I am messing up the class structure or what (I just learned classes like 3 days ago)
Here's my attempt...
class LinkedList:
class Node:
def __init__(self,value):
self.value = value
self.next = none
def __init__(self):
self.head = None
def findMin(self):
currentNode = self.head
minNode = self.head
while currentNode != none:
if currentNode.next < currentNode:
minNode = currentNode
currentNode = currentNode.next
return minNode
As #mgilson mentioned in comments, there are a number of things wrong, and not all were listed.
I think you would benefit a lot from writing (in comments, why not) what you think each line is doing.
Let's start with
currentNode = self.head
I think this is trying to say "let's start at the head of the list, by setting currentNode to point to that".
As written, this appears to be accessing a member variable of the current node, called 'head'. But the Node class definition doesn't have a defined member called head! And... why do you need to do that? You are told "the current node is the head"!
So you probably mean
currentNode = self # start at the head of the list
Next:
minNode = self.head
This is saying "The node with the current known minimum is stored in this one"
As before, you probably mean:
minNode = self # The head currently has the minimum we know about
Then:
while currentNode != none:
First, if you use a syntax highlighting editor, straight away it will tell you that 'None' has a capital 'N'.
No other problems here.
Then:
if currentNode.next < currentNode:
minNode = currentNode
currentNode = currentNode.next
This is trying to say "if the value of the next node is less than the value of this one, then the minimum is now ..." ... actually what? It's saying it's the current one! But it's not: if this if statement is true, then the next one contains the minimum! And hang on, shouldn't we be comparing with minNode, not currentNode?
Seems like you mean
if currentNode.value < minNode.value:
minNode = currentNode # this node is now the smallest we know about
and this next line needs to be outside the if loop, because it moves us on to the next node:
currentNode = currentNode.next # move on to the next node
Phew, nearly there: now we have to return the minimum value not, the node that has the minimum value (read the instructions carefullly.
return minNode.value
HTH
Ok, let's do this homework!
Let Node be a list cell. It has two fields value and next (also called car/cdr or head/tail in functional languages).
class Node:
def __init__(self, value):
self.value = value
self.next = None
append(node, other) can be defined as follows: if node has no "next" node (i.e. it is the last), attach other right to this node. Otherwise, take the "next" node, and append (recursively) other to that one:
def append(self, other):
if self.next:
self.next.append(other)
else:
self.next = other
Now let's define two essential functionals: map and reduce. map applies a function to each node in order and returns the list of results:
def map(self, fn):
r = [fn(self.value)]
if self.next:
r += self.next.map(fn)
return r
reduce applies a function to each node and combines results into a single value:
def reduce(self, fn, r=None):
if r is None:
r = self.value
else:
r = fn(r, self.value)
if self.next:
return self.next.reduce(fn, r)
return r
Now you're ready for the homework.
Create a list:
lst = Node(1)
lst.append(Node(8))
lst.append(Node(3))
lst.append(Node(7))
print all values:
print lst.map(lambda x: x)
find the last value:
print lst.reduce(lambda r, x: x)
find the max value:
print lst.reduce(max)
etc
Let us know if you have questions.

Search function for a n-ary tree using python

I am trying to build a search function for the n-ary tree. Here is how node class is :
class node(object):
"""docstring for node"""
def __init__(self, val=''):
self.val = val #value of the node
self.subtrees = [] #list of subtree node objects
following is the code how I call search function:
temp_search(root, "1")
And there is one node whose value is "1". And I expect node object to be returned on successful search.
following is my first search function I implemented:
#temp_search v0.1
def temp_search(node, key):
if node.val == key:
print 'Found', node
return node
for subtree in node.subtrees:
return temp_search(subtree, key)
The above thing returns 'None' and it never printed 'Found'
Now I modified it a bit:
#temp_search v0.2
def temp_search(node, key):
if node.val == key:
print 'found', node
return node
for subtree in node.subtrees:
temp_search(subtree, key)
Though it returns 'None' it still printed 'Found'. Okay thats some improvement.
So I realized that loop is running on each subtree object even after it returned the node. Does that make any sense? Because I think once it returned something, it should come out of it right? So modified it again:
#temp_search v0.3
def temp_search(node, key):
if node.val == key:
print 'Found', node
return node
for subtree in node.subtrees:
temp = temp_search(subtree, key)
if temp:
return temp
Similarly I implemented multi search like this [i.e. it should return all nodes whose value matches to key]
#temp_multi_search v0.1
def temp_multi_search(some_node, key, result=[]):
if some_node.val == key:
print 'found', some_node
return result.append(some_node)
for subtree in some_node.subtrees:
temp = temp_multi_search(subtree, key, result)
if temp:
result.append(temp)
return result
I would call above function like this:
temp_multi_search(root, "1")
But I got result as :
[<__main__.node object at 0x101eb4610>, [...], [...], [...], [...], [...], [...], [...], <__main__.node object at 0x101eb47d0>, [...], [...], [...], [...]]
So it was appending empty lists(?). This is how I fixed it:
#temp_multi_search v0.2
def temp_multi_search(some_node, key, result=[]):
#result = []
if some_node.val == key:
print 'found', some_node
return result.append(some_node)
for subtree in some_node.subtrees:
temp = temp_multi_search(subtree, key, result)
if isinstance(temp, node):
result.append(temp)
return result
Now I would get correct, expected result :
[<__main__.node object at 0x10b697610>, <__main__.node object at 0x10b6977d0>]
Here are my questions :
Why temp_search v0.1 failing? When going to through each subtree, when the search result is found, the value is returned. Why the loop is not getting terminated?
In temp_search v0.2, I don't have return statement in for loop. So what difference here it is making? Since the object is being found, but how do I return it successfully?
Have I implemented temp_search v0.3 in a right way? Any improvements?
Why temp_multi_search v0.1 is failing?
Have I implemented temp_multi_search v0.2 in a right way? Any improvements?
Here is what I think:
I think the for loop is running is only once. It searches in the first subtree only (recursively), which returns if item not present it returns none. I confirmed this via sending a value which was present in first subtree
Here for loop is successfully running on each subtree, thats why it is able to find the node. But how do I make it return the node other method than temp_search v0.3? I find temp_search v0.3 less pythonic
-
I think it is failing because the return temp would be some list, so it is just appending it to result.
-
Your answer is correct. Since the function call on first subtree always returns (regardless the value is found or not), it won't check for next subtrees.
You don't have return statement, so at the end of the function Python will implicitly insert return None
This is already optimal. There is no single Python statement that can do that.
The lists (not empty list!) are added because you do return result, and so the parent function call will receive a list (of results) as the result. Then it will append to its local copy of result, and return.
You can improve this by not giving result as a parameter:
#temp_multi_search v0.25
def temp_multi_search(some_node, key):
result = [] # Line 1
if some_node.val == key:
print 'found', some_node
result.append(some_node) # Line 2
for subtree in some_node.subtrees:
result.extend(temp_multi_search(subtree, key)) # Line 3
return result
Explanation:
It will first check the value on the root node, if it doesn't match, we don't append that node to the search result, otherwise, we add that to our result so far (which would only contain itself). Next we check every subtree.
Now, we already know that the function temp_multi_search(subtree, key) will return all occurrences on that tree. So after we call temp_multi_search(subtree, key) on each child, we extend the result found in that subtree to our result so far (which might have included the results from previous children).
At the end of iterating all children, we return our result.
Example:
Suppose we are looking for the number 1 in the following tree, and expects the function to return all occurrences.
0
_______|________
| | |
1 2 3
_|_ _|_ ___|___
| | | | | | |
1 4 1 5 6 1 7
So first we call temp_multi_search(root,1). It's not 1, so result is still empty now.
Next we check each subtree:
Child 1: The node value matches, so we add it into the result (at Line 2). Now say we have result = [node1]. Then check each subtree:
GrandChild 1: The node value matches, so we add it into the result (at Line 2). No more subtrees. Return [node2]. The Child 1 call will extend the result [node2] into his result [node1] (Line 3). So the Child 1 now has [node1, node2].
GrandChild 2: Node value doesn't match. No more subtrees. Return []. Child 1 extend empty list, so no change.
Child 2: Doesn't match. Check each subtree:
GrandChild 3: Match. Add to result (Line 2). No more subtrees. Return [node5]. Child 2 becomes [node5] (Line 3).
GrandChild 4: Doesn't match. No more subtrees. Return []. Child 2 still [node5].
Child 3: Doesn't match. Check each subtree:
GrandChild 5: Doesn't match. No more subtrees. Return []
GrandChild 6: Match Add to result (Line 2). Return [node9]. Child 3 will extend it to be [node9] (Line 3)
GrandChild 7: Doesn't match. No more subtrees. Return [].
At the end of each mentioned step, the returned result will be extended to Root result by Line 3. So after Step 1, Root result is [node1, node2]. After Step 2, Root result is [node1, node2, node5]. After Step 3, Root result is [node1, node2, node5, node9].
Then after checking all children, return the result.

Return tuple of list in recursion function

I am learning Python's recursion. I define a linked list where each node has item and next. I want to write a recursion to put odd and even number in a seperate set.
class LinkNode(object):
"""A node in a linked list."""
def __init__(self, item, next=None):
"""(LinkNode, object, LinkNode) -> NoneType
Initialize this node to store item and next.
"""
self.item = item
self.next = next
def odd_or_even(self):
"""(LinkNode) -> ([object], [object])
Return a pair of lists: (odd number, even number.
"""
if self is None:
return ([], [])
else:
if (self.item % 2 == 1):
odd_num = odd_num.append(self.item)
else:
even_num = even_num.append(self.item)
if self.next is not None:
self.next.odd_or_even()
return (odd_num, even_num)
When I run it, i got the following error:
File "C:\Program Files\Wing IDE 101 4.1\src\debug\tserver_sandbox.py", line 19, in odd_or_even
builtins.UnboundLocalError: local variable 'odd_num' referenced before assignment
Where should i initial odd_num, even_num so it won't be overwritten?
Thanks.
I think there are a few different approaches you could use.
One would be to use global variables. I don't really recommend this, but it is easy to understand, so I'm presenting it first:
even_num = [] # these should be at module level, but I'm not showing the class
odd_num = [] # so you'll have to imagine the indentation being correct
def odd_or_even(self):
"""(LinkNode) -> ([object], [object])
Return a pair of lists: (odd number, even number.
"""
if self.item % 2 == 1:
odd_num.append(self.item)
else:
even_num.append(self.item)
if self.next is not None:
self.next.odd_or_even()
The changes to the code are minor, mostly just removing return statements. I did take out the initial check for self being None, as that's never possible in a method (unless the caller tries very hard to make it happen). It's also worth noting that we do not attempt to assign to odd_num or even_num directly, as that would create a local variable, rather than accessing the global variables that already exist. The downside of this solution is that you can only call odd_or_even once and have it work properly. If you want to call it again (perhaps on another list) you need to reinitialize global variables.
Here's a better approach, which creates even and odd lists as local variables, and then returns them at the end. This is probably what you were aiming for in your code, but you were missing the step of adding the results of the recursive call onto the list.
def odd_or_even(self):
even_num = []
odd_num = []
if self.item % 2 == 1:
odd_num.append(self.item)
else:
even_num.append(self.item)
if self.next is not None:
next_even, next_odd = self.next.odd_or_even()
even_num.extend(next_even)
odd_num.extend(next_odd)
return even_num, odd_num
The problem with this code is that creating and extending lists is wasteful. On each level of the recursion, two new lists will be created, even though only one value is going to be handled at that level. A better approach can use just two lists total, for the whole recursive process:
def odd_or_even(self, lists=None):
if lists is not None:
even_num, odd_num = lists
else:
even_num = []
odd_num = []
if self.item % 2 == 1:
odd_num.append(self.item)
else:
even_num.append(self.item)
if self.next is not None:
return self.next.odd_or_even((even_num, odd_num))
else:
return even_num, odd_num
This is more efficient than the previous version, since it uses the same list on all levels of the recursion. In some other programming languages, this style of recursion, where all the work is done before the recursive call is run, is much more efficient than other kinds of recursion. This is because a compiler can optimize away the return step of the function (and just reuse the stack frame). However, Python doesn't do such "tail call optimization" (because it would mess up stack traces) so the benefits are not as big as they might be.
Another thing to think about: You could use your own linked-list class, rather than Python lists to hold the even and odd values. Both the second and third solutions I showed above could work, though the third would work most naturally if you were willing to have the even and odd values returned in reverse order.
if (self.item % 2 == 1):
odd_num = odd_num.append(self.item)
else:
even_num = even_num.append(self.item)
...
return (odd_num, even_num)
The above section of code sets the value of either odd_num or even_num, but not both. It then attempts to return both odd_num and even_num, which produces an error for one or the other. If you initialize both to None just before that if statement, you'll avoid the error that occurred. However, to make the semantics work, you may need to add another parameter to your function, namely the result from the previous level; in the recursive call, pass the just-computed odd_num or even_num down to the next level; then return the result of the next level down. However, it might be better to avoid recursion and instead have a loop that runs twice.

Recursively Navigating Through Tree In Python

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)

Categories

Resources