I wrote this code to print path from root to target node in binary tree using recursive DFS:
def dfs(self, current, target, path=[]):
if current == None:
path = []
return False
elif current.val == target.val:
return True
else:
path.append(current.val)
return self.dfs(current.left, target, path) or
self.dfs(current.right, target, path)
When I run this code, I notice the path variable has a list of all nodes that it traversed to get to the target node rather than just the direct path from root to target. What am I missing? I know I might be missing resetting the path variable somewhere, but I am not sure..
There are these issues:
A syntax issue (probably because you changed it in your post): the last statement is split over two lines, which is not allowed. If you want that, add parenthesis, or append
\after the first line of these two.path = [](inside the firstifblock) is a useless statement: it just assigns a new value to a localpathname, and never uses it again. Maybe you thought it would alter thepathof the caller, but assignment never does that in Python. On the other hand, if you mutate the list thatpathalready references, then that is the caller's list that mutates. For instance, this is what happensappend. But even if in this case you must empty the caller's list, that would not be the correct logic, since the target node might still be on a path that has some of the same ancestors. In short, thispath = []statement should just be removed.The path will get nodes appended as they are visited, even when the two recursive calls return
False, andFalseis returned for the current call, that node will still remain on the path. You should remove it from the path -- and only that node.Giving the
pathparameter a default value of[]is a bad idea. This means that if you don't pass this argument, this default list is being used, and it will still be that list if you make yet another call without that argument. This will mix up results from two different searches. Instead, you could useNoneas default, and have the code set it to a new empty list when it is found to beNone.Not a problem, but it would make sense to also append the target node to the path.
So with those fixes, your code would look like this:
Now it will work. Still, I would suggest some changes.
I would not use a
pathargument at all: it is non-intuitive that the original caller first has to create apath(set to an empty list) and then provide it to this function. The function should take care of that itself.Have the function return the path. The boolean that you return now can be removed: instead return
Noneif the target was not found, and a list (the path) when the target was found.Instead of appending and then popping again (because the target was not yet found), you could only start building the path when you have actually found the target, and then while unwinding from recursion, add the ancestor nodes to that path. So instead of building the path in preorder fashion, build it in postorder fashion.
Here is how that idea can be implemented:
With this variant, the caller must not provide a
pathargument, but instead gets the path as return value. They should check whether thispathisNone, which would indicate the target node was not found.