This one gives the correct result:
def binaryTree(root):
paths = []
def dfs(root, path=""):
if root:
if path != "":
path += "->"
path += str(root.val)
if not root.left and not root.right:
paths.append(path)
dfs(root.left, path)
dfs(root.right, path)
dfs(root)
return paths # gives ['1->2->4', '1->2->5', '1->3']
And in this one the list of path keeps growing:
def binaryTree2(root):
paths = []
def dfs(root, path=[]):
if root:
path.append(root.val)
if not root.left and not root.right:
paths.append("->".join(map(str, path)))
dfs(root.left, path)
dfs(root.right, path)
dfs(root)
return paths # gives ['1->2->4', '1->2->4->5', '1->2->4->5->3']
The tree is like this: <1, <2, <4, None, None>, <5, None, None>>, <3, None, None>>
The only difference is that in one I concatenate strings and in the other I append to list.
So in the first implementation: All path += ... statements essentially create a new string and have path point to it.
As for the second implementation you have a single list that is passed around all the time. You should pop back the node right before dfs returns.
def binaryTree2(root):
paths = []
def dfs(root, path=[]):
if root:
path.append(root.val)
if not root.left and not root.right:
paths.append("->".join(map(str, path)))
dfs(root.left, path)
dfs(root.right, path)
path.pop() # this clears your stack as your functions return
dfs(root)
return paths
Edit: Python strings are immutable - i.e. once created, they can't be modified.
# below line essentially creates a pointer,
# and a string object that `path` points to.
path = "huhu"
# this creates another string object `huhu123`.
# So at this point we have 3 strings objects,
# "123", "huhu" and "huhu123". And a pointer `path` if you will.
# `path` points to "huhu123"
path += "123"
If we had more innocent objects instead of strings, once they are left with no references, they'd be garbage collected. Strings get special treatment, in our case all 3 of them are interned.
Related
I've been looking into for quite some time , but can't come up with a proper solution.
Sure we can sort a list of integers to form a BST with the help of a recursive function and call it a day, but there's just too many operations being performed.
I did create a simple function that forms a BST from an unsorted list of integers, but it isn't through recursion.
class:
class Tree:
def __init__(self,key):
self.left = None
self.right = None
self.key = key
function which displays the Tree(rotated 90°)
def display_keys(node, space='\t', level=0):
# print(node.key if node else None, level)
# If the node is empty
if node is None:
print(space*level + '∅')
return
# If the node is a leaf
if node.left is None and node.right is None:
print(space*level + str(node.key))
return
# If the node has children
display_keys(node.right, space, level+1)
print(space*level + str(node.key))
display_keys(node.left,space, level+1)
Source for the display function
function which takes up a single element and adds it to the Tree...
def gen_BST(node,key):
if node is None:
node = Tree(key)
elif key < node.key:
node.left = gen_BST(node.left,key)
else:
node.right = gen_BST(node.right,key)
return node
int_list = [4,2,7,5,1,9,8,6,3]
tree = None
for val in int_list:
tree = gen_BST(tree,val)
Display:
display_keys(tree)
So once again, is there any way to create a recursive function which to form a BST from an unsorted list of integers.
Edit:
The function should take up the entire list as an argument.
I am getting a different result when I am using append(path) vs. append(list(path))
I have the following code to find all paths for a sum:
class TreeNode:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def find_paths(root, sum):
allPaths = []
dfs(root, sum, [], allPaths)
return allPaths
def dfs(root, sum, path, res):
if not root:
return
path.append(root.val)
if root.val == sum and root.left is None and root.left is None:
res.append(path)
dfs(root.left, sum - root.val, path, res)
dfs(root.right, sum - root.val, path, res)
del path[-1]
def main():
root = TreeNode(12)
root.left = TreeNode(7)
root.right = TreeNode(1)
root.left.left = TreeNode(4)
root.right.left = TreeNode(10)
root.right.right = TreeNode(5)
sum = 23
print("Tree paths with sum " + str(sum) +
": " + str(find_paths(root, sum)))
main()
This has the following output:
Tree paths with sum 23: [[], []]
But if I change the res.append(path) to res.append(list(path)) which will then return the correct answer Tree paths with sum 23: [[12, 7, 4], [12, 1, 10]]. I am confused on why using the list operation would change the answer.
res.append(path) appends the object path itself to the list res. After that line, when you modify the path object (like del path[-1]), the modification is also applied to the appended object in res, because, well, they are the same object.
list(path), on the other hand, "copies" the path. So this one is now a different object from path. When you modify path after that, the modification does not propagates to this different object.
You will have the same result if you do path[:] or path.copy() instead of list(path).
res.append(path) appends the actual path object, not a copy of it. So if path changes later on, the change will appear in res also.
res.append(list(path)) appends a copy.
I have written some code that conducts a BFS when a graph is defined. I now need to Edit the code to do a DFS, does anyone know what i need to change to complete this?
This is the BFS code i have now:
class MyQUEUE: # Just an implementation of a queue.
def __init__(self): # Initialises the queue to an empty queue.
self.holder = []
def enqueue(self,val):
# Appends item val onto the end of the queue.
self.holder.append(val)
def dequeue(self):
# Pops the head off the queue and returns it value.
val = None
try:
val = self.holder[0]
if len(self.holder) == 1:
self.holder = []
else:
self.holder = self.holder[1:]
except:
pass
return val
def IsEmpty(self):
# Returns True if the queue is empty.
result = False
if len(self.holder) == 0:
result = True
return result
def BFS(graph,start,end):
# Traverses the graph using a Breadth First Search starting
# from the start node with end being the goal node. Uses a
# queue to store the nodes that are current leaves at the
# fringe of the search.
q = MyQUEUE() # make an empty queue first
q.enqueue([start]) # add the start node onto the queue
while q.IsEmpty() == False:
path = q.dequeue()
last_node = path[len(path)-1]
print (path)
if last_node == end:
print ("VALID_PATH : ", path)
for link_node in graph[last_node]:
if link_node not in path:
new_path = []
new_path = path + [link_node]
q.enqueue(new_path)
Instead of append - insert at the beginning:
modify:
self.holder.append(val)
to:
self.holder.insert(0, val)
Explanation
While BFS "pills" the neighbors "layer by layer" a DFS goes "all the way to the bottom" and back. By changing the order we iterate the neighbors (recursively) we are, in fact, modifying the BFS into DFS.
UPDATE
As Kaya commented below, if we care about performance we should use append/pop instead of insert because insert takes O(n) while append/pop are O(1)
I have a function that finds all paths through a graph. The function returns a list of all paths. How do I use this value later in my code?
def findpaths(attachednodes,startID,endID,path = []):
path = path + [startID]
if startID == endID:
return [path]
if startID not in attachednodes:
return []
paths = []
for n in attachednodes[startID]:
if n not in path:
newpath = findpaths(attachednodes,n,endID,path)
for new in newpath:
paths.append(new)
for i in range(len(paths)):
numflight = i
flight = paths[i]
flights.update({numflight: flight})
return paths
you put the function call to the right side of a variable assignment. The variable will have the return value:
e.g.
def some_function():
return 10
x = some_function()
print(x) # will print 10
I was trying to print all paths(root-to-leaf paths) of binary tree but of no avail.
My strategy is to use the recursion, having the base case as either tree is None or tree node is leaf return Otherwise, traverse through left and right of the tree.
But I can't find a way to retain both of left and right tree.
def pathSum(self, root, target, result):
if not root:
return []
if not root.left and not root.right:
return [root.val]
for i in [root.left, root.right]:
path = [root.val] + self.pathSum(i, target, result)
print("path", path)
return path
The idea is building the path (list) at each node visit, if current node is a leaf, add current to path and print it, if no, just add current to extend the path:
def pathSum(self, path):
if not self.left and not self.right:
print(path + [self.val])
return
self.left.pathSum(path + [self.val])
self.right.pathSum(path + [self.val])
root.pathSum([])
Update: If you want to keep all paths:
def pathSum(self, current_path, all_paths):
if not self.left and not self.right:
print('Path found: ' + str(current_path + [self.val]))
all_paths.append(current_path + [self.val])
return
self.left.pathSum(current_path + [self.val], all_paths)
self.right.pathSum(current_path + [self.val], all_paths)
all_paths = []
root.pathSum([], all_paths)
print('All paths: ' + str(all_paths))
Through some iterations, I found the following solution works. But I'm not sure if there's a more efficient way of finding all leaf-root paths.
The idea behind this solution is pre-order traversal
def allPaths(self, root, path, all_path):
if not root.left and not root.right:
path.append(root.val)
all_path.append(path[:])
return
if root:
path.append(root.val)
self.allPaths(root.left, path, all_path)
path.pop(-1)
self.allPaths(root.right, path, all_path)
path.pop(-1)
return all_path