seek the lower ancestor in a binary tree - python

I wanna find the lower ancestor in a binary tree, and what i do is first, list the fathers of each node, then compare the list and the last item in common is the lower ancestry.
I have this code:
def ancesters(self, node, list= []):
if self.seekNode(node) != False:
if node < self.id:
list.append(self.id)
return self.left.ancesters(node)
elif node > self.id:
list.append(self.id)
return self.right.ancesters(node)
elif self.id == node:
list.append(self.id)
return list
else:
return False
the function seekNode works, and this method too, but when i use the method twice, shows the list of ancestor of the last call, example:
i have this tree:
2
|
---
5
---
3 6
and when i call the method ancesters(6), the list will be (2,5,6), and when i call again to search the fathers of 3, shows (2,5,6,2,5,3).
so, when i set the parameter (list=[]) why the list does not initialize and save the list value?.
I call the method with the same object, in this case will be the node (root) of the tree. the nodes are instance of the node (root) of the tree.

In short, do not use mutable object (like list) as a default value.
This is Anti-Pattern, because in Python the default value is shared between all function calls. so it must be immutable. popular option is None and condition if not None: ...
You can also read here

Related

How does the referencing work in python and how to create a sperate copy of linkedlist to compare with original linkedlist

class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
regularListHead = ListNode(-1)
regularListHead.next = head
reverseListHead = head
reverseListPrev = None
reverseListCurrent = reverseListHead
while reverseListCurrent != None:
reverseListNext = reverseListCurrent.next
reverseListCurrent.next = reverseListPrev
reverseListPrev = reverseListCurrent
reverseListCurrent = reverseListNext
reverseListHead = reverseListPrev
a = regularListHead
In my code, I am trying to convert the original list to reversed list and compare both for checking palindrome but when I do the operations to reverse the reverselist then original list is also updated. I am new to python can anyone let me know why this is happening and what could be done to achieve what I want
You only create one new node. All the other nodes are the nodes of your original list, and you update their next properties so the list is reversed.
So if you want to have both the original and the reversed list, you'll have to have two lists, each having their own nodes:
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None:
return
# Create reversed list
revHead = ListNode(head.val)
current = head.next
while current is not None:
revHead = ListNode(current.val, revHead)
current = current.next
# Compare original with the reversed one
while head is not None:
if head.val != revHead.val:
return False
head = head.next
revHead = revHead.next
return True
This is however not optimal: the comparison loop should only need to perform 𝑛/2 comparisons, not 𝑛.
You may even ask yourself why you would create a linked list for the reversed version: as it is only needed for the palindrome check, you might as well create a standard list, and then do the palindrome check with just that list, which is a piece of cake.
But more importantly, creating a new list (whether a linked list or normal "array" list) requires O(n) extra space. If you are interested in an algorithm for doing this without creating new nodes, then try to implement this:
Count the number of nodes in the list
Use that to identify the first node of the second half of the list. If the number of nodes is odd, let this be the node after the center node.
Apply an in-place list reversal algorithm on that second half. Now you have two shorter lists.
Compare the values in those two lists to see whether they are equal (ignore the center node if there was one). Remember the outcome (false or true)
Optionally repeat step 3 so the reversal is rolled back, and the list is back in its original state. (This is "nice" towards the caller of your function)
Return the result that was found in step 4.

Object list index is not being updated: Index Error: list

I have a class which I will keep omit unimportant information:
class BuildMap:
def __init__(self):
#Constructor
self.nodes = []
def indexmap(self):
for node in self.nodes:
print(int(node),":",nodes[node])
def delnode(self,num):
del self.nodes[num]
for edge in self.edges:
if num in self.edges[edge]:
self.edges[edge].remove(num)
del self.edges[num] #Deletes the list associated with the number
del nodes[int(num)] #Deleted the word associated with the node number
for dist in list(self.distance.keys()):
if num in dist:
del self.distance[dist]
Outside of the class:
def delnode():
print("Please specify the city you would like to remove: ")
GPSMap.indexmap()
num = input("City Number: ")
if int(num) >= len(nodes):
print("Sorry that number does not match")
delnode()
GPSMap.delnode(int(num))
The problem I am having is that after deleting one "node" and doing all the code I've made to clean up my other data structures I receive
File "C:/Users/gibbo/PycharmProjects/main.py", line 52, in indexmap
print(int(node),":",nodes[node])
IndexError: list index out of range
I have done some debugging and noticed my index isn't stopping with the length of the list and is going past after an node has been deleted as if the index hasn't been updated or my for loop:
for node in self.nodes:
Isn't using the number of nodes in the list as the index.
node is a node, not an integer. Since you haven't included the definition of the object, we don't know what happens when you convert it to an integer, but I'm pretty sure you're not going to like the result. When you use that as an index, the result is almost certain to be well outside the range of your list.
To see the actual effect, separate your print statement:
print(int(node))
print(nodes[node])
Overall, you should recognize that node is the node from the list, not its position in the list. Use node directly as the object you want.
def indexmap(self):
for node in self.nodes:
# list elem -^ ^- list
print(int(node),":",nodes[node])
# ^- It should be int, not elem.
so, fix it.
def indexmap(self):
for (i,node) in enumerate(self.nodes):
print("{}:{}".format(i, node))

What's wrong with this least common ancestor algorithm?

I was asked the following question in a job interview:
Given a root node (to a well formed binary tree) and two other nodes (which are guaranteed to be in the tree, and are also distinct), return the lowest common ancestor of the two nodes.
I didn't know any least common ancestor algorithms, so I tried to make one on the spot. I produced the following code:
def least_common_ancestor(root, a, b):
lca = [None]
def check_subtree(subtree, lca=lca):
if lca[0] is not None or subtree is None:
return 0
if subtree is a or subtree is b:
return 1
else:
ans = sum(check_subtree(n) for n in (subtree.left, subtree.right))
if ans == 2:
lca[0] = subtree
return 0
return ans
check_subtree(root)
return lca[0]
class Node:
def __init__(self, left, right):
self.left = left
self.right = right
I tried the following test cases and got the answer that I expected:
a = Node(None, None)
b = Node(None, None)
tree = Node(Node(Node(None, a), b), None)
tree2 = Node(a, Node(Node(None, None), b))
tree3 = Node(a, b)
but my interviewer told me that "there is a class of trees for which your algorithm returns None." I couldn't figure out what it was and I flubbed the interview. I can't think of a case where the algorithm would make it to the bottom of the tree without ans ever becoming 2 -- what am I missing?
You forgot to account for the case where a is a direct ancestor of b, or vice versa. You stop searching as soon as you find either node and return 1, so you'll never find the other node in that case.
You were given a well-formed binary search tree; one of the properties of such a tree is that you can easily find elements based on their relative size to the current node; smaller elements are going into the left sub-tree, greater go into the right. As such, if you know that both elements are in the tree you only need to compare keys; as soon as you find a node that is in between the two target nodes, or equal to one them, you have found lowest common ancestor.
Your sample nodes never included the keys stored in the tree, so you cannot make use of this property, but if you did, you'd use:
def lca(tree, a, b):
if a.key <= tree.key <= b.key:
return tree
if a.key < tree.key and b.key < tree.key:
return lca(tree.left, a, b)
return lca(tree.right, a, b)
If the tree is merely a 'regular' binary tree, and not a search tree, your only option is to find the paths for both elements and find the point at which these paths diverge.
If your binary tree maintains parent references and depth, this can be done efficiently; simply walk up the deeper of the two nodes until you are at the same depth, then continue upwards from both nodes until you have found a common node; that is the least-common-ancestor.
If you don't have those two elements, you'll have to find the path to both nodes with separate searches, starting from the root, then find the last common node in those two paths.
You are missing the case where a is an ancestor of b.
Look at the simple counter example:
a
b None
a is also given as root, and when invoking the function, you invoke check_subtree(root), which is a, you then find out that this is what you are looking for (in the stop clause that returns 1), and return 1 immidiately without setting lca as it should have been.

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.

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