What time and space complexity am I using with memoized countUnivalTrees problem - python

I was attempting this challenge but took too long to execute for some inputs. The question is as follows
You are given a binary tree. Return the count of unival sub-trees in the given binary tree. In unival trees, all the nodes, below the root node, have the same value as the data of the root.
My first attempt failed because of the time complexity
def countUnivalTrees(tree):
if tree is None:
return 0
leftCount = countUnivalTrees(tree.left)
rightCount = countUnivalTrees(tree.right)
count = leftCount + rightCount
if isUnivalTree(tree, tree.value):
count += 1
return count
def isUnivalTree(tree, value):
if tree is None:
return True
if tree.value != value:
return False
if tree.left is None and tree.right is None:
return True
return isUnivalTree(tree.left, value) and isUnivalTree(tree.right, value)
It gives the right answer but can take too long to execute for some given inputs.
My Second approach was to memoize what I've seen already.
def countUnivalTrees(root, memoized={}):
if root is None:
return 0
memoized[root] = False
leftCount = countUnivalTrees(root.left, memoized)
rightCount = countUnivalTrees(root.right, memoized)
count = leftCount + rightCount
if root.left is not None and root.right is not None:
if memoized[root.left] and memoized[root.right] and root.left.value == root.right.value and root.value == root.left.value:
memoized[root] = True
count += 1
elif root.left is not None:
if memoized[root.left] and root.value == root.left.value:
memoized[root] = True
count += 1
elif root.right is not None:
if memoized[root.right] and root.value == root.right.value:
memoized[root] = True
count += 1
else:
if isUnivalTree(root, root.value):
memoized[root] = True
count += 1
return count
The isUnivalTree is indentical to the first attempt so I omitted it to not add the extra code. I was able to pass all tests. My plan was to have a time complexity of O(n) and O(n) space. I am having trouble understanding exactly what my complexity would be and if my approach is correct. By correct I mean working exactly how I believe it should be:
step 1: visit each node in the tree
step 2: from leaf to root check current root is unival or not. If it is save the root in memoized and map it to true.
step 3: if current root node has a left and right child node and that node in memoized maps to true meaning it is a unival tree than just check right and left child values to be the same and root value to be the same and is a unival tree memoize the current root and map to true. (also take into account for roots that have only one left child or only one right child and perform those checks as well O(1) operations)
So I'm imagining a big tree with many spanning left and right but with all the same values. Is it true that we are looking at all the nodes once O(n), where n is the number of nodes and then as we pop off the stack memoizing that current node and mapping to true if isUnivalTree and so on.
For some reason I feel like I might be missing a case where I should check if memoized[root.left] = False and if it is then not count it. Not sure really, I never memoized before besides for the fibonacci coding problem so this is new to me.

We can't see the memoization because we don't see your isUnivalTree. But if it is fast, then it is certainly O(n) time because you're only visiting each node a maximum of 2x (once to count trees, once to see if it was marked unival), on one of which you memoize it.
But there is no need to memoize. Just return all of the context that you need in one go. And if that is messier than you want to use, then have a trivial wrapper.
def unival_trees (root):
return _unival_trees_and_meta(root)[0]
def _unival_trees_and_meta(root):
if root.left is None:
if root.right is None:
return (1, True, root.value)
else:
count_right, right_is_unival, right_value = _unival_trees_and_meta(root.right)
if right_is_unival and root.value == right_value:
return (count_right + 1, True, root.value)
else:
return (count_right, False, root.value)
else:
count_left, left_is_unival, left_value = _unival_trees_and_meta(root.left)
if root.right is None:
if left_is_unival and left_value == root.value:
return (count_left + 1, True, root.value)
else:
return (count_left, False, root.value)
else:
count_right, right_is_unival, right_value = _unival_trees_and_meta(root.right)
if left_is_unival and right_is_unival and left_value == right_value and left_value == root.value:
return (count_right + count_left + 1, True, root.value)
else:
return (count_right + count_left, False, root.value)

Related

Pacman minimax algorithm - my algorithm makes the wrong choice

When i do the task - I pass all the tests except from the fact that my choice is not the same as the optimal. I dont understand why that is - or what i need to change in my code. I have tried everything, but there seeems to be a fundamental error somewhere in the algorith that makes my algorith choose Right even tho the best action is Left
def max_value(self, gameState, depth, agentIndex):
pacIndex = 0 #packmans index
bound = float('-inf') #create an upper bound
#check if we have a terminal state or leaf node
if gameState.isWin() or gameState.isLose() or depth == self.depth:
return self.evaluationFunction(gameState), None
if gameState.getNumAgents() == 0:
value, a = self.max_value(gameState, depth + 1,agentIndex)
#check for all childrennodes for packman
for action in gameState.getLegalActions(pacIndex):
#find each of the succesor from the action node
sucessor = gameState.generateSuccessor(pacIndex,action)
#now we minimize the next agent (ghost)
ghost_index = pacIndex + 1
value, result = self.min_value(sucessor,depth,ghost_index)
if value > bound: #if we have a higher one
bound = max(value,bound) #new boud, max of what we have found
maxresult = action
#return (bound,maxresult) [depth == 1]#return actions done for depth 1 now we move down
return bound, maxresult
def min_value (self, gameState, depth ,agentIndex):
bound = float('inf') #lower bound inifinity
minresult = None
agentNumber = gameState.getNumAgents() # find out whos turn it is
#check if we have terminal state
if gameState.isLose() or depth == self.depth:
return self.evaluationFunction(gameState), None
#get all the legal actions for the agent we are currently on
for action in gameState.getLegalActions(agentIndex):
#create the next nodes one step down
sucessor = gameState.generateSuccessor(agentIndex, action)
#check if there are ghosts left
if agentIndex == agentNumber-1:
#check if there are one the last node
#then we want to maximise for the packman #recccursive lol
pacIndex = 0
value, maxresult = self.max_value(sucessor,depth + 1,pacIndex)
else:
#for the ghosts
value, result = self.min_value(sucessor,depth,agentIndex+1)
if value < bound :
bound = min(value,bound)
minresult = action
return bound, minresult
#now the minmax function
def minimax(self,gameState):
depth = 0
a, maxresult = self.max_value(gameState, 0, 0)
return maxresult
```

how to programming pre / in order traversal without recursion in python

here is my code about postorder_nonrecursive.
I want to know preorder and inorder like this code.
please help me!
def postorder_nonrecursive(self):
if self == None:
return
stack = [[self,0]]
while len(stack) > 0:
node, state = stack[-1]
if state == 2:
yield node
stack.pop()
else:
child = (node.getLeft() if state == 0 else node.getRight())
stack[-1][1] += 1
if child != None:
stack.append([child,0])

How to check whether the sum exists for a given path in a tree

I want to check if the sum of values in any path from the start node to the
leaf node exists.
For example suppose I have startNode is say a 7 and the sumTarget is 15 if the tree is:
a-7
b - 1 e- 8
c - 2
d -9
Then since 7 +8 equals 15 it would return true
If I have b as the startNode and 12 as the sumTotal then it would also return true because 1 +2 + 9 is 12 starting with b.
class Node {
int value;
Node [] children
}
I don't think this is right, but I'm not sure what is wrong.
def doesSumExist(startNode, sumTarget, currentSum):
totalSum = sumTarget
if startNode is not Null:
if totalSum + startNode.value == sumTarget:
return True
else:
totalSum += startNode.value
else:
Node startNode = doesSumExist(startNode.left, sumTarget, currentSum)
if startNode is not Null:
return currentSum
startNode = doesSumExist(startNode.right, sumTarget,currentSum)
return False
In that case I think what you're searching for is something like the following:
def doesSumExist(startNode, sumTarget, currentSum):
totalSum = currentSum
if startNode is not Null:
if totalSum + startNode.value == sumTarget: #If this node completes the sum
return True
else: #if not
totalSum += startNode.value #increase current sum
if doesSumExist(startNode.left, sumTarget, totalSum): #recursive starting on the left children
return True
elif doesSumExist(startNode.right, sumTarget, totalSum): #recursive starting on the right children
return True
return False #if the sum is not present (starting in startNode).
However, this does not check if any successive combination of nodes contains the sum (the code would much more complex).
Hope this helps
assuming that your node class looks something like this:
class Node:
def __init__(self, value = 0, children = None):
self.value = value
self.children = [] if children is None else children
then this method should do the trick:
def doesSumExist(startNode, targetSum, currentSum):
if startNode is None:
return False
currentSum += startNode.value
if currentSum == targetSum:
return True
for child in startNode.children:
if doesSumExist(child, targetSum, currentSum):
return True
return False
Note that for this Node-class design the None-check of startNode isn't really necessary for the recursion but only for the entry point. So this would probably be better:
def doesSumExist(startNode, targetSum):
def inner(node, targetSum, currentSum):
currentSum += node.value
if currentSum == targetSum:
return True
#for people who like to save a few lines
#return any(inner(child, targetSum, currentSum) for child in node.children)
for child in node.children:
if inner(child, targetSum, currentSum):
return True
return False
if startNode is None:
return False
return inner(startNode, targetSum, 0)
Edit:
If you not only want to know if the sum exists in a path from your start node but also if it would exist in any given sub path this should work:
def doesSumExist(startNode, targetSum):
def inner(node, targetSum, allValues):
allValues.append(node.value)
currentSum = 0
for val in reversed(allValues):
currentSum += val
if currentSum == targetSum:
return True
for child in node.children:
if inner(child, targetSum, allValues):
return True
allValues.pop()
return False
if startNode is None:
return False
return inner(startNode, targetSum, [])

Python - Converting an n-ary tree to a binary tree

class Tree:
def __init__(self, new_key):
self.__key = new_key # Root key value
self.__children = [] # List of children
self.__num_of_descendants = 0 # Number of Descendants of this node
# Prints the given tree
def printTree(self):
return self.printTreeGivenPrefix("", True)
# Prints the given tree with the given prefix for the line
# last_child indicates whether the node is the last of its parent"s child
# or not
def printTreeGivenPrefix(self, line_prefix, last_child):
print(line_prefix, end="")
if last_child:
print("â””--> ", end="")
else:
print("|--> ", end="")
print(self.__key)
if len(self.__children) > 0:
next_pre = line_prefix
if last_child:
next_pre += " "
else:
next_pre += "| "
for child_index in range(len(self.__children)-1):
self.__children[child_index].\
printTreeGivenPrefix(next_pre, False)
self.__children[-1].printTreeGivenPrefix(next_pre, True)
def __repr__(self):
return "[" + str(self.__key) + "".join(
[ repr(child) for child in self.__children ]) + "]"
# This static function will load a tree with the format of below:
# [root[child_1][child_2]...[child_n]]
# Each child_i can be a tree with the above format, too
# pos is the position in the given string
#staticmethod
def loadTree(tree_str, pos = 0):
new_node = None
while pos < len(tree_str):
if tree_str[pos] == "[":
pos += 1
new_node = Tree(tree_str[pos])
while pos < len(tree_str) and tree_str[pos + 1] != "]":
pos += 1
child_tree, pos = Tree.loadTree(tree_str, pos)
if child_tree:
new_node.__children.append(child_tree)
new_node.__num_of_descendants += \
1 + child_tree.__num_of_descendants
return new_node, pos + 1
else:
pos += 1
return new_node, pos
def find_largest(self):
if self.__num_of_descendants == 1:
return self.__children[0]
else:
largest_child = self.__children[0]
for child in self.__children:
if child.__num_of_descendants > \
largest_child.__num_of_descendants:
largest_child = child
if child.__num_of_descendants == \
largest_child.__num_of_descendants:
if child.__key > largest_child.__key:
largest_child = child
return largest_child
def convert_to_binary_tree(self):
if self.__num_of_descendants != 0:
if self.__num_of_descendants < 3:
for child in self.__children:
child.convert_to_binary_tree()
if self.__num_of_descendants > 2:
left_child = self.__children[0]
for child in self.__children[1:]:
if len(child.__children) > len(left_child.__children):
left_child = child
elif len(child.__children) == len(left_child.__children):
if child.__key > left_child.__key:
left_child = child
self.__children.remove(left_child)
self.__num_of_descendants -= 1
right_child = self.__children[0]
for child in self.__children[1:]:
if len(child.__children) > len(right_child.__children):
right_child = child
elif len(child.__children) == len(right_child.__children):
if child.__key > right_child.__key:
right_child = child
self.__children.remove(right_child)
self.__num_of_descendants -= 1
print(self.__num_of_descendants)
print(self.__children)
print(left_child)
print(right_child)
#Move remaining children two either left_child or right_child.
while self.__num_of_descendants != 0:
largest_child = self.find_largest()
print(largest_child)
if left_child.__num_of_descendants < \
right_child.__num_of_descendants:
left_child.__children.append(largest_child)
left_child.__num_of_descendants += 1
self.__children.remove(largest_child)
self.__num_of_descendants -= 1
elif left_child.__num_of_descendants > \
right_child.__num_of_descendants:
right_child.__children.append(largest_child)
right_child.__num_of_descendants += 1
self.__children.remove(largest_child)
self.__num_of_descendants -= 1
elif left_child.__num_of_descendants == \
right_child.__num_of_descendants:
if left_child.__key > right_child.__key:
left_child.__children.append(largest_child)
left_child.__num_of_descendants += 1
self.__children.remove(largest_child)
self.__num_of_descendants -= 1
else:
right_child.__children.append(largest_child)
right_child.__num_of_descendants += 1
self.__children.remove(largest_child)
self.__num_of_descendants -= 1
#Now run recursion on left and right binary children.
self.__children.append(left_child)
self.__children.append(right_child)
self.__num_of_descendants = 2
print(self.__children)
for child in self.__children:
child.convert_to_binary_tree()
def main():
tree, processed_chars = Tree.loadTree('[z[y][x][w][v]]]')
tree.convert_to_binary_tree()
tree.printTree()
print(tree)
if __name__ == "__main__":
main()
I have to convert a given tree into a binary tree. If a node in the tree has more than 2 children, I have to assign the child with the most descendants as the left node and the child with the second largest number of descendents as the right child. The remaining children are added as following:
1) Take child with largest number of descendants
2) Add it to Left/Right node. Whichever has fewer children at that time.
*If at any time I need to select the child with the largest number of descendants, but there are two+ with the same number of descendents, I take the one with the larger key value.
I get a print out like this...
2 #Number of 'z' children after left and right node chosen.
[[w], [v]] #Children of 'z'
[y] #Binary left child of 'z'
[x] #Binary right child of 'z'
[w] #This is a bug. It should be choosing 'v' as larger child of 'z' and assigning it to left child 'y'
[v] #This is a bug. see above.
[[y[w]], [x[v]]] #These are the children of node 'z'
â””--> z #schematic of binary tree
|--> y
| â””--> w
â””--> x
â””--> v
[z[y[w]][x[v]]] #final binary tree
DSM's comment helped me see what is going on. After you pick left_child and right_child in the first parts of your convert_to_binary_tree method, you're not removing them from the list of children. This means that later, when you go to add all of the current node's children into new parents, you're adding the left and right children to themselves (or each other). When you recurse into those children, you can end up infinitely looping.
I don't really understand the logic of your left_child and right_child selections, so I don't have fixed code to suggest to you. A quick but ugly fix would be to put a if child in (left_child, right_child): continue statement at the top of the for loop where you're assigning the other children to new parents.
Note that there's another bug in your current code, where the descendent counts for the left and right children will become incorrect. That's because you're not updating the count when you push some of their former siblings into them as children.

Counting number of nodes in a Binary Search Tree

I am trying to print the size of my binary search tree. However, what my code is doing now is printing what level each node is at. I feel as though I am putting my count += 1 in the wrong place or perhaps it might be something else. I was wondering if someone could be an extra set of eyes for me and give me a hint on how I can fix this.
output:
3
3
2
3
3
2
1
expected output:
7
my code:
def BST_size(root, count = 0):
if root is None:
print "size -1 (Null value in root)"
if root is not None:
count += 1
if root.left is not None:
BST_size(root.left, count)
if root.right is not None:
BST_size(root.right, count)
print count
Extra parts:
class Node:
def __init__(self,value):
self.right = None
self.left = None
self.value = value
def BST_Insert(root, node): # root --> root of tree or subtree!
if root.value is None:
root = node # beginning of tree
else:
if root.value > node.value: # go to left
if root.left is None:
root.left = node
else:
BST_Insert(root.left, node)
if root.value < node.value: # go to right
if root.right is None:
root.right = node
else:
BST_Insert(root.right, node)
r = Node(4)
# left
a = Node(2)
b = Node(1)
c = Node(3)
# right
d = Node(8)
e = Node(6)
f = Node(10)
BST_Insert(r, a)
BST_Insert(r, b)
BST_Insert(r, c)
BST_Insert(r, d)
BST_Insert(r, e)
BST_Insert(r, f)
There are two ways to do this.
The easy way is to return the count from each recursive call, instead of just printing it, and increment:
def BST_size(root):
if root is None:
return -1
if root is not None:
if root.left is not None:
return 1 + BST_size(root.left)
if root.right is not None:
return 1 + BST_size(root.right)
def print_BST_size(root):
size = BST_size(root)
if size == -1:
print "size -1 (Null value in root)"
else:
print "size", size
However, that still doesn't work, because you're only traversing the left side or the right side, not both. What you want is:
count = -1
if root is not None:
if root.left is not None:
count += BST_size(root.left)
if root.right is not None:
count += BST_size(root.right)
return count
However, it looks like you're trying to use an accumulator, in tail-recursive style. There's really no point in doing this in Python, because Python doesn't optimize tail calls, but it's a useful skill to learn for other languages and for theoretical reasons, so…
The problem here is that you need the same value to be shared by all of the recursive calls, which means you need a mutable value. So, you can start with [0] instead of 0, and do count[0] += 1 instead of count += 1.
Or you can rearrange your code so that it doesn't use count mutably, and instead passes the updated count from one side down to the other.
Either way, you've got another problem with your code. Your recursive base case is that root is None. But you've also made it a special case, printing -1 instead of 0. You can't have it both ways.
def BST_size(root, count = 0):
if root is None:
return count
return BST_size(root.left, BST_size(root.right, count + 1))
def BST_size(root):
if root is None:
return 0
else:
return BST_size(root.left) + BST_size(root.right) + 1
print "Size is: " + str(BST_size(r))
if root is not None:
count = 1
if root.left is not None:
count += BST_size(root.left, count)
if root.right is not None:
count += BST_size(root.right, count)
return count
Here is a short answer
You have to do tree treversal, where the function returns the number of nodes below + 1 (itself)
Pseudo code:
def counter(t):
rCount = counter(t.right)
lCount = counter(t.left)
return 1 + rCount + lCount
You could use something like this:
def countNodes(root):
count = 1
if root is None:
return -1
if root is not None:
if root.left is not None:
count += countNodes(root.left)
if root.right is not None:
count += countNodes(root.right)
return count
and invoke it like below:
print ("\nCount of Nodes :" + str(countNodes(root)))

Categories