Is it possible to repeat a method in a for loop? - python

so I am writing a code to find the height of a binary search tree. My first thoughts were to traverse through both the right and left subtrees, append the values of the nodes in each subtree to a list, and then get the length of the list, and the longer list would be the height of the tree. Here is my code:
def getHeight(self,root):
lstr = []
lstl = []
if root.right is not None:
lstr.append(root.right.data)
if root.right.right is not None:
lstr.append(root.right.right.data)
if not root.left == None:
lstl.append(root.left.data)
if root.left.left is not None:
lstr.append(root.left.left.data)
return lstr
return lstl
However, is it possible to use a for loop to keep iterating through the .right.right.right and just continue using the .right attribute in the if statement until the for loop ends?

Related

Checking if a binary tree is symmetric around its centre- how to print node values as a debug?

I am writing code to solve the following leetcode problem:
https://leetcode.com/problems/symmetric-tree/
The problem in a nutshell is "Given the root of a binary tree, check whether it is a mirror of itself (i.e., symmetric around its center)."
from collections import deque
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
queue= deque()
if not root:
return []
#add left and right child of root to start (if root is not None)
if root.left:
queue.append(root.left)
if root.right:
queue.append(root.right)
right_subt = []
left_subt = []
while queue:
level_length = len(queue)
#iterate over each level of the tree and add left subtree to left_subt and right subtree to right_subt
for _ in range((level_length//2)+1):
node = queue.popleft()
left_subt.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
for _ in range((level_length-(level_length//2))+1):
node = queue.popleft()
right_subt.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
#compare left and right subtree at each level to check if they're the same
if left_subt !=right_subt.reverse():
return False
#reinitialize left and right subtree lists
left_subt = []
right_subt = []
return True
I have run the algorithm with the following input: [1,2,2,3,4,4,3] ; it is the input that you see at the top of the page when you click on the link to Leetcode above.
It is returning false when it should be returning true. When I run the input on paper it seems to work, but unlike arrays/strings, I am not sure how to print node values at each stage. Any ideas how to do this, or can someone please outline where the code falls short?
I am not sure how to print node values at each stage.
printing values in python is as easy as:
print(<something>)
where <something> is whatever you need to print
for debug purposes you can at any point in your code add for example:
print(node.val)
or
print(queue)
At least the following snippet will not work as you expected
for _ in range((level_length//2)+1):
node = queue.popleft()
left_subt.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
Right at the 1st loop, at which, level_length == 2 (assume that both root.left & root.right are not None )
-> (level_length//2)+1 will return (1 + 1)
-> The 1st for statement will loop two times, and consume both root.left & root.right in your queue before the 2nd for statement starts
-> nothing's left for your right_subt
-> In the 2nd for statement, queue.popleft() will throw an exception due to the fact that you tried to 'pop from an empty deque'

Find Ancestors of a given Node in a Binary Tree

I was attempting to solve the question "Find Ancestors of given node". For a fact I was able to solve the question. But I had an add-on question for which I am unable to alter the logic
So lets assume the Binary tree is like below :
100
/ \
45 150
/ \ / \
40 60 120 200
The common ancestor for Node 60 would be 100,45.
My Code :
def helper(node,k,path):
if node is None:
return path
path.append(node.data) # Append every node as we visit
if path[-1]==k: #Once we find the desired Node, print the list till last element
print(path[:-1])
path = helper(node.left,k,path)
path = helper(node.right,k,path)
del path[-1] #while backtracking , remove the leaf node
return path
def findCommonAns(root,k):
if root is None:
return None
if root.data == k:
return [root.data]
helper(root,60,[])
findCommonAns(tree.root,60)
My add-on question is, instead of printing, if I wanted to return the list (path), how can I modify this logic ? As I am learning recursion for just a week now, I am finding it hard to modify the above logic.
Any help is much appreciated. Thanks !
def helper(node,k,path):
#If node is empty, that is if either root is None,
#or if we have gone beyon leaf node, result the path list
if node is None:
return path
path.append(node.data) #Append data as we visit
if path[-1]==k:#If last item of the list is the node
return path[:-1] # return the elemenet till the last element
path = helper(node.left,k,path)
path = helper(node.right,k,path)
del path[-1]
return path
def findCommonAns(root,k):
if root is None:
return None
if root.data == k:
return [root.data]
path=[]
helper(root,k,path)
return path[:-1]
findCommonAns(tree.root,60)

Sort a linked list, handling pairs as a single item

I am working on this challenge:
Sort a Linked list in increasing order by considering two nodes as a two digit number. (in place sorting)
4 ≤ n ≤ 1000
The length of the linked list is always even.
Example 1:
Input
1→3→4→2→1→2
Output
1→2→1→3→4→2
Explanation:
12 > 13 > 42
Example 2:
Input
1→3→0→3
Output
0→3→1→3
Here is my linked list template implementation; which anyone can start coding:
class Node():
def __init__(self,val):
self.data = val
self.next = None
class Linkedlist():
def __init__(self):
self.head = None
def add(self,x):
t = Node(x)
t.next = self.head
self.head = t
def display(self):
p = self.head
while p != None:
print(p.data)
p=p.next
def sort(self):
curr = self.head
# your logic here
m = Linkedlist()
m.add(1)
m.add(3)
m.add(4)
m.add(2)
m.add(1)
m.add(2)
m.display()
Which is the algorithm to sort the pairs in a linked list (in place), and how can I code it?
First write a function that accepts a linked list of single digit nodes and merges each pair of adjacent single digit nodes into a linked list of half as many nodes, where each node contains a double digit number.
Then sort the resulting linked list using bubble sort, which is O(n^2), or mergesort, which is O(nlogn).
Then, if need be, write a third function that takes apart the double digit nodes and creates a new list of twice as many single digit nodes.
Doing it with two or three functions like this will really simplify the sort.
First of all, your current code will not generate list 1→3→4→2→1→2, but its inverse. This is because your definition of the add method will prepend the given value to the list.
To make add work correctly, it should first find the last node in the list, and append the new node after it. As this is not a very efficient method to add many values to a list, I would suggest to also define add as a Node method, and to allow chaining add calls. That way you can add many values in one chained expression without being inefficient.
Also, as you will need to compare values of pairs, it makes sense to make a method on Node that will return the value of the pair formed by that node and the next in the list.
Finally, for the sort algorithm itself, you could reduce the list to its first pair only (which is then obviously sorted), and treat the rest as a separate list of unsorted nodes. Then extract a pair at a time from the unsorted list, and find the right place to inject it in the main list, so that it remains sorted.
This process represents O(n²) time complexity on average and in the worst case. The best case is O(n), which occurs when the list is originally sorted in reverse order.
Here is how that could look:
class Node():
def __init__(self, val):
self.data = val
self.next = None
# Easy chaining: defining this method on a Node as well
def add(self, val):
node = Node(val)
node.next = self.next
self.next = node
return node
# Define how the value of a pair is calculated
def pairvalue(self):
return self.data * 10 + self.next.data
class Linkedlist():
def __init__(self):
self.head = None
def add(self, val):
# Corrected method: addition should be at the end of the list
node = Node(val)
if not self.head:
self.head = node
else: # Look for the last node in the list
curr = self.head
while curr.next:
curr = curr.next
# ...and append the new node after it
curr.next = node
return node # Allow for chaining
def display(self):
p = self.head
while p:
print(p.data, end= "→")
p = p.next
print("NIL")
def sort(self):
if not self.head:
return
# Reduce list to just its first pair
# The remainder is a temporary "todo" list
todo = self.head.next.next
self.head.next.next = None
while todo:
pair = todo # The pair that will be added to the sorted list
todo = todo.next.next
val = pair.pairvalue()
if val < self.head.pairvalue():
# The pair is a minimum: prepend it
pair.next.next = self.head
self.head = pair
else:
curr = self.head.next # odd index
# Find insertion point in sorted list
while curr.next and curr.next.pairvalue() < val:
curr = curr.next.next
# Perform insertion
pair.next.next = curr.next
curr.next = pair
m = Linkedlist()
m.add(1).add(3).add(4).add(2).add(1).add(2)
m.display() # original list
m.sort()
m.display() # final list
The output:
1→3→4→2→1→2→NIL
1→2→1→3→4→2→NIL

while statement with stack in Inorder Traversal

When I want to save all nodes into an inorder list sorted_node_val, I use while statement.
def closestKValues(self, root, target, k):
# write your code here
stack = []
sorted_node_val = []
node = root
while node:
stack.append(node)
node = node.left
while stack is not None:
node = stack.pop()
sorted_node_val.append(node.val)
if node.right:
node = node.right
while node:
stack.append(node)
node = node.left
However the code above yields error with while stack is not None: and the result is
File "/Users/Python/901.py", line 35, in closestKValues
node = stack.pop()
IndexError: pop from empty list
I changed while statement into while stack: and fixed this error.
But I wonder what's the difference between while stack is not None: and while stack:
At the very end of your stack, stack = []. This is an list of length 0, not a none object. You can verify this by trying None == [], which will be False.
You can check this condition out, by running a Python IDE and creating an empty array. Then, with the empty array, run a condition check:
temp = []
temp is None
The output is False.
This tells us that an empty array is not considered to be None, which makes sense because an array is not of a null type.

common_ancestor function for nested tree dictionary in python

I am trying to create a function called "common_ancestor()" that takes two inputs: the first a list of string taxa names, and the second a phylogenetic tree dictionary. It should return a string giving the name of the taxon that is the closest common ancestor of all the
species in the input list. Already made a separate function called "list_ancestors" that gives me the general ancestors of the elements in the list. Also, have a dictionary I am working with.
tax_dict = {
'Pan troglodytes': 'Hominoidea', 'Pongo abelii': 'Hominoidea',
'Hominoidea': 'Simiiformes', 'Simiiformes': 'Haplorrhini',
'Tarsius tarsier': 'Tarsiiformes', 'Haplorrhini': 'Primates',
'Tarsiiformes': 'Haplorrhini', 'Loris tardigradus':'Lorisidae',
'Lorisidae': 'Strepsirrhini', 'Strepsirrhini': 'Primates',
'Allocebus trichotis': 'Lemuriformes', 'Lemuriformes': 'Strepsirrhini',
'Galago alleni': 'Lorisiformes', 'Lorisiformes': 'Strepsirrhini',
'Galago moholi': 'Lorisiformes'
}
def halfroot(tree):
taxon = random.choice(list(tree))
result = [taxon]
for i in range(0,len(tree)):
result.append(tree.get(taxon))
taxon = tree.get(taxon)
return result
def root(tree):
rootlist = halfroot(tree)
rootlist2 = rootlist[::-1]
newlist = []
for e in range(0,len(rootlist)):
if rootlist2[e] != None:
newlist.append(rootlist2[e])
return newlist[0]
def list_ancestors(taxon, tree):
result = [taxon]
while taxon != root(tree):
result.append(tree.get(taxon))
taxon = tree.get(taxon)
return result
def common_ancestors(inputlist,tree)
biglist1 = []
for i in range(0,len(listname)):
biglist1.append(list_ancestors(listname[i],tree))
"continue so that I get three separate lists where i can cross reference all elements from the first list to every other list to find a common ancestor "
the result should look something like
print(common_ancestor([’Hominoidea’, ’Pan troglodytes’,’Lorisiformes’], tax_dict)
Output: ’Primates’"
One way would be to collect the all ancestors for each species, place them in a set and then get an intersection to get what they have in common:
def common_ancestor(species_list, tree):
result = None # initiate a `None` result
for species in species_list: # loop through each species in the species_list
ancestors = {species} # initiate the ancestors set with the species itself
while True: # rinse & repeat until there are leaves in the ancestral tree
try:
species = tree[species] # get the species' ancestor
ancestors.add(species) # store it in the ancestors set
except KeyError:
break
# initiate the result or intersect it with ancestors from the previous species
result = ancestors if result is None else result & ancestors
# finally, return the ancestor if there is only one in the result, or None
return result.pop() if result and len(result) == 1 else None
print(common_ancestor(["Hominoidea", "Pan troglodytes", "Lorisiformes"], tax_dict))
# Primates
You can use the 'middle' part of this function for the list_ancestors(), too - there is no need to complicate it by trying to find the tree's root:
def list_ancestors(species, tree, include_self=True):
ancestors = [species] if include_self else []
while True:
try:
species = tree[species]
ancestors.append(species)
except KeyError:
break
return ancestors
Of course, both rely on a valid ancestral tree dictionary - if some of the ancestors were to recurse on themselves or if there is a breakage in the chain it won't work. Also, if you were to do a lot of these operations it might be worth to turn your flat dictionary into a proper tree.

Categories