Depth First Search returns only root value - python

I am trying to get a list of values of the tree using DFS but I only get root's value. :(
def depthFirstSearch(root):
output = []
if root:
output.append(root.value)
depthFirstSearch(root.left)
depthFirstSearch(root.right)
return output

A bit late perhaps, but if you look at what you do it makes sense that only your root is returned.
Specifically, you append the root.value in the first iteration, then you run depthFirstSearch for both children (in a binary tree, presumably), but here's the thing: You just discard the result. It will probably work if you concatenate the results from both recursive calls to the output before returning.
That would get you something like:
def depthFirstSearch(root):
output = []
if root:
output.append(root.value)
# use the += operator to concatenate the current output list and the new one from the subtree
output += depthFirstSearch(root.left)
output += depthFirstSearch(root.right)
return output

Related

Concatenating tree data - How to simplify my code?

I solved an exercise where I had to apply a recursive algorithm to a tree that's so defined:
class GenericTree:
""" A tree in which each node can have any number of children.
Each node is linked to its parent and to its immediate sibling on the right
"""
def __init__(self, data):
self._data = data
self._child = None
self._sibling = None
self._parent = None
I had to concatenate the data of the leaves with the data of the parents and so on until we arrive to the root that will have the sum of all the leaves data. I solved it in this way and it works but it seems very tortuous and mechanic:
def marvelous(self):
""" MODIFIES each node data replacing it with the concatenation
of its leaves data
- MUST USE a recursive solution
- assume node data is always a string
"""
if not self._child: #If there isn't any child
self._data=self._data #the value remains the same
if self._child: #If there are children
if self._child._child: #if there are niece
self._child.marvelous() #reapply the function to them
else: #if not nieces
self._data=self._child._data #initializing the name of our root node with the name of its 1st son
#if there are other sons, we'll add them to the root name
if self._child._sibling: #check
current=self._child._sibling #iterating through the sons-siblings line
while current:
current.marvelous() #we reapplying the function to them to replacing them with their concatenation (bottom-up process)
self._data+=current._data #we sum the sibling content to the node data
current=current._sibling #next for the iteration
#To add the new names to the new root node name:
self._data="" #initializing the root str value
current=self._child #having the child that through recursion have the correct str values, i can sum all them to the root node
while current:
self._data+=current._data
current=current._sibling
if self._sibling: #if there are siblings, they need to go through the function themselves
self._sibling.marvelous()
Basically I check if the node tree passed has children: if not, it remains with the same data.
If there are children, I check if there are nieces: in this case I restart the algorithm until I can some the leaves to the pre-terminal nodes, and I sum the leaves values to put that sum to their parents'data.
Then, I act on the root node with the code after the first while loop, so to put its name as the sum of all the leaves.
The final piece of code serves as to make the code ok for the siblings in each step.
How can I improve it?
It seems to me that your method performs a lot of redundant recursive calls.
For example this loop in your code:
while current:
current.marvelous()
self._data += current._data
current = current._sibling
is useless because the recursive call will be anyway performed by the last
instruction in your method (self._sibling.marvelous()). Besides,
you update self._data and then right after the loop you reset
self._data to "".
I tried to simplify it and came up with this solution that seems to
work.
def marvelous(self):
if self.child:
self.child.marvelous()
# at that point we know that the data for all the tree
# rooted in self have been computed. we collect these
self.data = ""
current = self.child
while current:
self.data += current.data
current = current.sibling
if self.sibling:
self.sibling.marvelous()
And here is a simpler solution:
def marvelous2(self):
if not self.child:
result = self.data
else:
result = self.child.marvelous2()
self.data = result
if self.sibling:
result += self.sibling.marvelous2()
return result
marvelous2 returns the data computed for a node and all its siblings. This avoids performing the while loop of the previous solution.

Sum of all Nodes Iteratively - Not Recursively - Without 'left' and 'right'

I have this Binary Tree Structure:
# A Node is an object
# - value : Number
# - children : List of Nodes
class Node:
def __init__(self, value, children):
self.value = value
self.children = children
I can easily sum the Nodes, recursively:
def sumNodesRec(root):
sumOfNodes = 0
for child in root.children:
sumOfNodes += sumNodesRec(child)
return root.value + sumOfNodes
Example Tree:
exampleTree = Node(1,[Node(2,[]),Node(3,[Node(4,[Node(5,[]),Node(6,[Node(7,[])])])])])
sumNodesRec(exampleTree)
> 28
However, I'm having difficulty figuring out how to sum all the nodes iteratively. Normally, with a binary tree that has 'left' and 'right' in the definition, I can find the sum. But, this definition is tripping me up a bit when thinking about it iteratively.
Any help or explanation would be great. I'm trying to make sure I'm not always doing things recursively, so I'm trying to practice creating normally recursive functions as iterative types, instead.
If we're talking iteration, this is a good use case for a queue.
total = 0
queue = [exampleTree]
while queue:
v = queue.pop(0)
queue.extend(v.children)
total += v.value
print(total)
28
This is a common idiom. Iterative graph traversal algorithms also work in this manner.
You can simulate stacks/queues using python's vanilla lists. Other (better) alternatives would be the collections.deque structure in the standard library. I should explicitly mention that its enque/deque operations are more efficient than what you'd expect from a vanilla list.
Iteratively you can create a list, stack, queue, or other structure that can hold the items you run through. Put the root into it. Start going through the list, take an element and add its children into the list also. Add the value to the sum. Take next element and repeat. This way there’s no recursion but performance and memory usage may be worse.
In response to the first answer:
def sumNodes(root):
current = [root]
nodeList = []
while current:
next_level = []
for n in current:
nodeList.append(n.value)
next_level.extend(n.children)
current = next_level
return sum(nodeList)
Thank you! That explanation helped me think through it more clearly.

Get selected node names into a list or tuple in Nuke with Python

I am trying to obtain a list of the names of selected nodes with Python in Nuke.
I have tried:
for s in nuke.selectedNodes():
n = s['name'].value()
print n
This gives me the names of the selected nodes, but as separate strings.
There is nothing I can do to them that will combine each string. If I
have three Merges selected, in the Nuke script editor I get:
Result: Merge3
Merge2
Merge1
If I wrap the last variable n in brackets, I get:
Result: ['Merge3']
['Merge2']
['Merge1']
That's how I know they are separate strings. I found one other way to
return selected nodes. I used:
s = nuke.tcl("selected_nodes")
print s
I get odd names back like node3a7c000, but these names work in anything
that calls a node, like nuke.toNode() and they are all on one line. I
tried to force these results into a list or a tuple, like so:
s = nuke.tcl("selected_nodes")
print s
Result: node3a7c000 node3a7c400 node3a7c800
s = nuke.tcl("selected_nodes")
s2 = s.replace(" ","', '")
s3 = "(" + "'" + s2 + "'" + ")"
print s3
Result: ('node3a7c000', 'node3a7c400', 'node3a7c800')
My result looks to have the standard construct of a tuple, but if I try
to call the first value from the tuple, I get a parentheses back. This
is as if my created tuple is still a string.
Is there anything I can do to gather a list or tuple of selected nodes
names? I'm not sure what I am doing wrong and it seems that my last
solution should have worked.
As you iterate over each node, you'll want to add its name to a list ([]), and then return that. For instance:
names = []
for s in nuke.selectedNodes():
n = s['name'].value()
names.append(n)
print names
This will give you:
# Result: ['Merge3', 'Merge2', 'Merge1']
If you're familiar with list comprehensions, you can also use one to make names in one line:
names = [s['name'].value() for s in nuke.selectedNodes()]
nodename = list()
for node in nuke.selectedNodes():
nodename.append(node.name())

Binary search tree insertion Python

What is wrong with my insert function? I'm passing along the tr and the element el that I wish to insert, but I keep getting errors...
def insert( tr,el ):
""" Inserts an element into a BST -- returns an updated tree """
if tr == None:
return createEyecuBST( el,None )
else:
if el > tr.value:
tr.left = createEyecuBST( el,tr )
else:
tr.right = createEyecuBST( el,tr )
return EyecuBST( tr.left,tr.right,tr)
Thanks in advance.
ERROR:
ValueError: Not expected BST with 2 elements
It's a test function that basically tells me whether or not what I'm putting in is what I want out.
So, the way insertion in a binary tree usually works is that you start at the root node, and then decide which side, i.e. which subtree, you want to insert your element. Once you have made that decision, you are recursively inserting the element into that subtree, treating its root node as the new root node.
However, what you are doing in your function is that instead of going down towards the tree’s leaves, you are just creating a new subtree with the new value immediately (and generally mess up the existing tree).
Ideally, an binary tree insert should look like this:
def insert (tree, value):
if not tree:
# The subtree we entered doesn’t actually exist. So create a
# new tree with no left or right child.
return Node(value, None, None)
# Otherwise, the subtree does exist, so let’s see where we have
# to insert the value
if value < tree.value:
# Insert the value in the left subtree
tree.left = insert(tree.left, value)
else:
# Insert the value in the right subtree
tree.right = insert(tree.right, value)
# Since you want to return the changed tree, and since we expect
# that in our recursive calls, return this subtree (where the
# insertion has happened by now!).
return tree
Note, that this modifies the existing tree. It’s also possible that you treat a tree as an immutable state, where inserting an element creates a completely new tree without touching the old one. Since you are using createEyecuBST all the time, it is possible that this was your original intention.
To do that, you want to always return a newly created subtree representing the changed state of that subtree. It looks like this:
def insert (tree, value):
if tree is None:
# As before, if the subtree does not exist, create a new one
return Node(value, None, None)
if value < tree.value:
# Insert in the left subtree, so re-build the left subtree and
# return the new subtree at this level
return Node(tree.value, insert(tree.left, value), tree.right)
elif value > tree.value:
# Insert in the right subtree and rebuild it
return Node(tree.value, tree.left, insert(tree.right, value))
# Final case is that `tree.value == value`; in that case, we don’t
# need to change anything
return tree
Note: Since I didn’t know what’s the difference in your createEyecuBST function and the EyecuBST type is, I’m just using a type Node here which constructer accepts the value as the first parameter, and then the left and right subtree as the second and third.
Since the binary doesn't have the need to balance out anything , you can write as simple logic as possible while traversing at each step .
--> Compare with root value.
--> Is it less than root then go to left node.
--> Not greater than root , then go to right node.
--> Node exists ? Make it new root and repeat , else add the new node with the value
def insert(self, val):
treeNode = Node(val)
placed = 0
tmp = self.root
if not self.root:
self.root = treeNode
else:
while(not placed):
if val<tmp.info:
if not tmp.left:
tmp.left = treeNode
placed = 1
else:
tmp = tmp.left
else:
if not tmp.right:
tmp.right = treeNode
placed = 1
else:
tmp = tmp.right
return
You can also make the function recursive , but it shouldn't return anything. It will just attach the node in the innermost call .

Error when Searching trees iterively

I need to search a tree by checking if the sum of the branches from a node is greater than zero. However, I'm running into a problem with the sum - I get a type error (int object is not callable) on the
branch_sum = [t[0] for t in current]
line. I thought it was because eventually I'll get a single node
current = [[1,'b']]
(for example), and so I added the if/else statement. I.e. I thought that I was trying to sum something that looked like this:
first = [1]
However, the problem still persists. I'm unsure of what could be causing this.
For reference, current is a list of lists, with the first slot is the node data the second slot is a node id (in the inner list). The group() function groups the data on a node based on the id of the sub-nodes (left subnodes have ids beginning with 1, right have ids beginning with 0).
The tree I'm searching is stored as a list of lists like:
tree = [[0, '1'], [1,'01'], [0,'001']]
i.e. it's a set of Huffman Codes.
from collections import deque
def group(items):
right = [[item[0],item[1][1:]] for item in items if item[1].startswith('1')]
left = [[item[0],item[1][1:]] for item in items if item[1].startswith('0')]
return left, right
def search(node):
loops = 0
to_crawl = deque(group(node))
while to_crawl:
current = to_crawl.popleft() # this is the left branch of the tree
branch_sum = 0
if len(current)==1:
branch_sum = sum([t for t in current])
else:
branch_sum = sum([t[0] for t in current])
if branch_sum !=0 :
l,r = group(current)
to_crawl.extendleft(r)
to_crawl.extendleft(l)
loops += 1
return loops
Here's what I'm trying to do:
GIven a tree, with a lot of the data being 0, find the 1. To do this, split the tree into two branches (via the group() function) and push onto deque. Pop a branch off the deque, then sum the data in the branch. If the sum is not zero split the branch into two sub branches, push the sub branches onto the deque. Keep on doing this until I've found the non-zero datum. I should end up with a single item of the form [1,'101'] in the deque when I exit.
I strongly assume that the error says
TypeError: 'int' object is not iterable
because you end up passing a 2-tuple as node to
to_crawl = deque(group(node))
which gives you a 2-element deque. Then
current = to_crawl.popleft()
gives you a single element (an integer) as current. This is clearly not iterable, which leads to the given error.
Side note: For brevity, you can use sum like this
sum(current)
instead of
sum([x for x in current])

Categories

Resources