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.
Related
I am building a simple A.I for a "spider game" (pretty much the same concept as the snake game but the moving logic is a bit different). I am trying to implement a BFS algorithm such that the spider finds the path that leads it to the ant. The algorithm seems to work for several iterations but when I run it outside of the debugger it gets a None value inside the node_list and that makes the other methods fail (since you cannot get the next move for anything).
This is the BFS algorithm:
def BFS(spider_state, ant_state):
goal = False
count = 0
initial_node = Node(spider_state, None, 0)
node_list = [initial_node]
initial_ant_state = ant_state
while goal == False or len(node_list) == 0:
e = node_list.pop(0)
future_ant_state = initial_ant_state
for i in range(0, e.depth):
future_ant_state = get_next_ant_move(border_choice, initial_ant_state)
for move in POSSIBLE_MOVES:
count += 1
next_node = Node(None, None, None)
next_node.state = get_next_spider_move(deepcopy(e.state), move)
next_node.parent = e
next_node.depth = e.depth + 1
if next_node.state == future_ant_state:
goal = True
break
else:
node_list.append(next_node)
return node_list
Node:
class Node():
def __init__(self, state, parent, depth):
self.state = state
self.parent = parent
self.depth = depth
Spider and ant are represented as a simple list of x and y positions:
spider = [15, 35]
ant = [20, 10]
The get next move methods look like this:
def get_next_spider_move(spidy, move):
if spidy:
# check the bounds and assign the new value to spidy
spidy = spider_bounds(spidy)
# farthest right
if move == 0:
spidy[1] += 2
spidy[0] -= 1
# furhter up and right
if move == 1:
spidy[1] += 1
spidy[0] -= 2
# backwords
if move == 2:
spidy[0] += 1
# farthest left
if move == 3:
spidy[1] -= 2
spidy[0] -= 1
# furhter up and to the left
if move == 4:
spidy[1] += 1
spidy[0] -= 2
# one left
if move == 5:
spidy[1] -= 1
# one right
if move == 6:
spidy[1] -= 1
# side right
if move == 7:
spidy[1] += 1
spidy[0] += 1
# side left
if move == 8:
spidy[1] -= 1
spidy[0] -= 1
else:
# if no valid direction was given
return spidy
else:
raise ValueError('spidy must contain an x and y position. %s', spidy, ' was found')
The resulting error when run:
File "spider_game_bfs.py", line 141, in <module>
path = BFS(spider, ant)
File "spider_game_bfs.py", line 130, in BFS
next_node.state = get_next_spider_move(deepcopy(e.state), move)
File "spider_game_bfs.py", line 100, in get_next_spider_move
raise ValueError('spidy must contain an x and y position. %s', spidy, ' was found')
ValueError: ('spidy must contain an x and y position. %s', None, ' was found')
You have a logic error at the bottom of your move function. The last complete statement is
if move == 8:
spidy[1] -= 1
spidy[0] -= 1
else:
# if no valid direction was given
return spidy
Your comment is incorrect: the else clause is executed by any move other than 8. If the move is 8, then you return None, as you've skipped the statement that returns spidy.
As the first comment mentioned, you will do better with if ... elif ... else as your logic structure. Even better than that, follow the many on-line examples for moving an item: make a list or dict of the moves, something like this:
move_dir = [
(-1, +2), # move 0
(-2, +1), # move 1
(+1, 0), # move 2
... # fill in the rest
]
if move in range(len(move_dir)):
spidy[0] += move_dir[move[0]]
spidy[1] += move_dir[move[1]]
return spidy
else:
raise ValueError ...
It seems that the child always sets itself to equal parent even though the conditions are not satisfied. Any help? Is this because of the layering?
def crack(ct):
temp = 10 + 0.087 * (len(ct)-84)
fit = ns.ngram_score('english_quadgrams.txt')
while 1 == 1:
parent = randkey()
counter = 0
while counter < 50000:
ps = fit.score((de(ct,parent)).upper())
number = rd.randint(1,25)
if number == 1:
child = nw(parent)
else:
if number == 2:
child = td(parent)
else:
if number == 3:
child = lr(parent)
else:
if number == 4:
child = randr(parent)
else:
if number == 5:
child = randc(parent)
else:
child = rand2(parent)
print(de(ct,child))
cs = fit.score((de(ct,child)).upper())
diff = ps-cs
print(ps,cs, diff)
prob = 1/(math.exp(diff/temp))
print(prob)
random = rd.uniform(0,1)
print(random)
if diff < 0:
parent = child
elif prob < random:
counter = counter + 1
else:
parent = child
The score is computed based on the quadgram rating texts and library based on here. Any advice would be helpful.
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, [])
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)))
I am trying to binary create a tree from a binary sequence like "100101"
then i want the tree to be created like this. (Basically 1 means go to the right and 0 means go to the left)
<Root node>
|
1
/
0
/
0
\
1
/
0
\
1
so i did it here is the code:
where the value would be the string sequence (ex value = "1001")
def _inserto(root,value):
for i in value:
if root == None:
root = BSTNode(i)
current = root
elif i == "1":
if current.right is not None:
current.right = BSTNode(i)
current = current.right
else:
current.right = BSTNode(i)
current = current.right
elif i == "0":
if (current.left is not None):
current.left = BSTNode(i)
current = current.left
else:
current.left =BSTNode(i)
current = current.left
return root
Now the problem is that if i want to input another sequence like "01", the tree should look like this
<Root Node>
|
1
/
0
/ \
0 1
\
1
/
0
\
1
, but i am really having a hard time, since my function is going to over-write the old tree.
The problem is with the code dealing with an existing node. If it is present, the code overwrites it with a new BSTNode, losing all the existing nodes under it. What you need is something like:
elif i == "1":
if current.right is None:
current.right = BSTNode(i)
current = current.right
elif i == "0":
if root.left is None:
current.left = BSTNode(i)
current = current.left
This will only allocate a node if there is not one already present, and then set current to this new node.