How to understand recursion in Binary Tree - python

I have started to learn Recursion , I seem to understand the concept , but at the same time I feel completely lost.
For eg, I was trying to solve find the ancestors of a given node in Binary Tree
My Tree:
1
/ \
7 9
/ \ / \
6 5 2 3
My code :
def findAns(node,target,ans):
if node is None:
return ans
ans.append(node.data)
if node == target:
return ans[:-1] #return the list except the last item(whch will be the target node)
ans = findAns(node.left,target,ans)
ans = findAns(node.right,target,ans)
del ans[-1] #Delete last node while backtracking
return ans
ans=[]
findAns(root,target,ans) #target node is 5
print(ans)
print(ans[:-1])
OUTPUT :
[1,7,5]
[1, 7]
I am unable to understand the below questions,
When backtracking is completed for the entire tree, list 'ans' will be empty # root position, how come I am able to get the value [1,7,5] in my list ?
when the if condition is satisfied , I return ans[:-1] , because I should not include the target node, but how come when I print 'ans' I am getting the target node as well ?
In the same 'if condition' , if I return ans instead of ans[:-1], y code doesn't work, I get back an empty 'ans' list, why is that ?
But to avoid the confusion I changed my code as mentioned below, I used a global variable . However I don't think it is efficient , any resource or material or explanations for my above question would be of great help to me. Thanks !
Code with Global variable:
_ans=[]
def findAns(node,target,ans):
global _ans
if node is None:
return ans
ans.append(node.data)
if node == target:
_ans.append(list(ans[:-1]))
#return ans[:-1]
ans = findAns(node.left,target,ans)
ans = findAns(node.right,target,ans)
del ans[-1]
return ans
ans=[]
findAns(root,target,ans)
_ans[0]

You expected the root after backtracking returns empty list, well it does. The problem is the function returns the empty list but you don't catch it:
res=findAns(root, target, ans)
print(res)
Output:
[]
From question 1 you may still think printing ans will be the same as catching the return value, but when you return ans[:-1] you have already lost the original list you passed in. This is heavily related to list in python being mutable and list[:-1] will return a completely new list and modifying the new list won't affect the ans you first passed in. So after it returns a new list, the backtracking ans = findAns(root.right, target, ans) takes a new list, after that changing the 'new' ans won't change the 'old' ans(the one used to print)
If you change to return ans then it will print out empty list. The problem is every time you iterate through a step in the recursion you append a new node but at the same time you delete one last node, which results in add one and delete one being zero.

Related

Python while loop not ending when list empty

I was trying to find out a way to perform in-order tree transversal iteratively without using a tuple. Not sure why but the list is going from [] to [None] and runs the while loop one more time
https://leetcode.com/problems/binary-tree-inorder-traversal/description/
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
stack = [root]
res = []
while stack:
print("start",stack)
curr = stack.pop()
if curr.left:
temp = curr.left
curr.left = None
if curr.right:
stack.append(curr.right)
curr.right = None
stack.append(curr)
stack.append(temp)
else:
res.append(curr.val)
if curr.right:
stack.append(curr.right)
print("end",stack)
return res
when running this code with testcase [1], the output when printing is
start [TreeNode{val: 1, left: None, right: None}]
end []
start [None]
this means at the end of the while loop stack = [], so the while loop should end. However, all of a sudden the stack = [None] and runs one more loop. Very confused why this happens. I put a print in front of every append but found nothing. It is solved by adding a check for if curr is None but I still have no idea why this happens. If anyone knows please help!
UPDATE:
Copy and pasted into hackerrank's same question and it worked properly. Could this be a bug in leetcode?????
https://www.hackerrank.com/challenges/tree-inorder-traversal/problem
I am assuming what you are trying to do here is convert a tree to an in-order list of elements to look at. It is possible you're trying to do something else, but if so, it is unclear.
def inOrderTraversal(ls: TreeNode) -> List[int]:
// Recurse into the left branch
if isinstance(ls.left, int):
left = [ls.left]
else: // Assumes left is a TreeNode
left = inOrderTraversal(ls.left)
// Recurse into the right branch
if isintance(ls.right, int):
right = [ls.right]
else:
right = inOrderTraversal(ls.right)
return left + right
This almost certainly won't work right out of the box, but it is a demonstration of what you're going for. You need to determine if the left branch should be recursed down, then the right. If the left or right branches are simply ints, then you need to add them as elements.
Your while loop is a misdirection - you are already setting stack to be a list of length one (the root element). The while loop isn't doing anything since you have only one element. You do not need to set anything to None for this sort of task - just forget about whatever you were iterating over once you have the data you need.

Python if else statement in recursive function getting stuck:

so I'm making a function that takes a string and the number of gaps that i need to insert in it, and i want it to output a list of strings of all possible combinations where those gaps are inserted.
I have written a recursive function for that, but the stopping if condition is not being activated no matter what i do. Even printing the expression in itself gives the proper answer but the if condition doesn't follow that expression.
I hope you guys can help with this, even though it's probably a very simple error on my part, i just cant seem to find it.
Thanks in advance.
f = open("bonusoutput.txt",'w')
sequence1 = raw_input("Sequence 1:")
sequence2 = raw_input("Sequence 2:")
l1 = int(len(sequence1))
l2 = int(len(sequence2))
#---------------Function that has problem-----------------------------
def insertBlanks(numGap,string):
if (numGap <= 0):
return [string]
else:
outSeq = []
for cp in range(0,len(string)+1):
outSeq.append(string[:cp] + "_" + string[cp:])
for seq in outSeq:
outSeq += (insertBlanks(numGap-1,seq))
return outSeq
#-------------------------------------------------------------
nGap1 = l2
nGap2 = l1
outSeq2 = insertBlanks(nGap1,sequence2)
f.write(str(outSeq2))
print outSeq2
While looping for seq in outSeq, you are appending items to outSeq. You're returning a list of at least one item each time (base case returns [string] therefore you will add at least 1 item for each item you visit, so you have an infinite loop. Consider adding your output to a new list (or using a list comprehension, like [insertBlanks(numGap - 1, seq) for seq in outSeq]

On Finding the Maximum Depth of an Arbitrarily Nested List

I'm currently working with a recursive function in Python, and I've run into a wall. As titled, the problem is to return the maximum depth of an arbitrarily nested list.
Here is what I have so far:
def depthCount(lst):
'takes an arbitrarily nested list as a parameter and returns the maximum depth to which the list has nested sub-lists.'
var = 0
if len(lst) > 0:
if type(lst[0]) == list:
var += 1
depthCount(lst[1:])
else:
depthCount(lst[1:])
else:
return var
I feel that the problem is with my recursive calls (this may be obvious). It will indeed return var when the list has reached the end, but when I have a nonempty list, things go awry. Nothing is returned at all.
Am I slicing wrong? Should I be doing something before the slice in the recursive call?
The problem may also be with my base case.
If they are just nested lists, e.g., [[[], []], [], [[]]], here's a nice solution:
def depthCount(lst):
return 1 + max(map(depthCount, lst), default=0)
Here's a slight variation you could use if you don't use Python 3.4, where the default argument was introduced:
def depthCount(lst):
return len(lst) and 1 + max(map(depthCount, lst))
They also differ by how they count. The first considers the empty list to be depth 1, the second to be depth 0. The first one is easy to adapt, though, just make the default -1.
If they're not just nested lists, e.g., [[[1], 'a', [-5.5]], [(6,3)], [['hi']]]), here are adaptions to that:
def depthCount(x):
return 1 + max(map(depthCount, x)) if x and isinstance(x, list) else 0
def depthCount(x):
return int(isinstance(x, list)) and len(x) and 1 + max(map(depthCount, x))
Make sure you understand how the latter one works. If you don't know it yet, it'll teach you how and works in Python :-)
Taking the "purely recursive" challenge:
def depthCount(x, depth=0):
if not x or not isinstance(x, list):
return depth
return max(depthCount(x[0], depth+1),
depthCount(x[1:], depth))
Granted, the extra argument is slightly ugly, but I think it's ok.
It will indeed return var when the list has reached the end, but when I have a nonempty list, things go awry. Nothing is returned at all.
That's because you have no return statement, except in the else base case for an empty list. And if you fall off the end of the function without hitting a return, that means the function returns None.
But you have another problem on top of that. You're starting var = 0, then possibly doing var += 1… but you're not passing that down into the recursive calls, or using any result from the recursive calls. So the recursive calls have no useful effect at all.
What you probably meant is something like this:
def depthCount(lst):
'takes an arbitrarily nested list as a parameter and returns the maximum depth to which the list has nested sub-lists.'
if len(lst) > 0:
if type(lst[0]) == list:
return 1 + depthCount(lst[1:])
else:
return depthCount(lst[1:])
else:
return 0
But this still isn't actually right. The depth count of a list is 1 more than the depth count of its deepest element. Just checking its second element won't do you any good; you need to check all of them. So, what you really want is something like this:
def depthCount(lst):
'takes an arbitrarily nested list as a parameter and returns the maximum depth to which the list has nested sub-lists.'
if isinstance(lst, list):
return 1 + max(depthCount(x) for x in lst)
else:
return 0
If you want to replace that iterative for x in lst with a second layer of recursion, of course you can, but I can't see any good reason to do so; it just makes the code more complicated for no reason. For example:
def max_child_count(lst):
if lst:
return max(depth_count(lst[0]), max_child_count(lst[1:]))
else:
return 0
def depth_count(lst):
if isinstance(lst, list):
return 1 + max_child_count(lst)
else:
return 0
This may still not be right. It definitely does the right thing for, e.g., [1, [2,3], [4, [5]]]. But what should it do for, say, []? I can't tell from your question. If it should return 0 or 1, you'll obviously need to change the if appropriately. If that's illegal input, then it's already doing the right thing. (And that should also answer the question of what it should do for, e.g., [[[], []], [], [[]]], but make sure you think through that case as well.)
So, essentially, the data structure that you're referring to is a k-ary tree, also known as n-ary tree, with arbitrary branching. Here's the code for determining the max. depth of a n-ary tree with arbitrary branching.
def maxdepth(tree):
if isleaf(tree):
return 1
maximum = 0
for child in children(tree):
depth = maxdepth(child)
if depth > maximum:
maximum = depth
return maximum + 1
You can see the code in action with different test inputs here.

Recursive Function in Python adding Odd Values in List

My task is to create a recursive function in Python that takes a list and a value of 0 as its inputs and then adds up all of the odd numbers on the list and returns that value. Below is the code that I have and it keeps returning that the list index is out of range. No matter what I do I can not get it to work.
def addodds2(x,y):
total=0
a=x[y]
while y<len(x):
if a%2!=0:
total+=a
return(addodds2(x,y+1))
else:
return(addodds2(x,y+1))
return(total)
print(addodds2([3,2,4,7,2,4,1,3,2],0))
Since you are trying to solve this recursively, I don't think you want that while loop.
When you are trying to solve a problem recursively, you need two parts: you need a part that does some of the work, and you need a part that handles reaching the end of the work. This is the "basis case".
Often when solving problems like this, if you have a zero-length list you hit the basis case immediately. What should be the result for a zero-length list? I'd say 0.
So, here's the basic outline of a function to add together all the numbers in a list:
Check the length, and if you are already at the end or after the end, return 0. Otherwise, return the current item added to a recursive call (with the index value incremented).
Get that working, and then modify it so it only adds the odd values.
P.S. This seems like homework, so I didn't want to just give you the code. It's easier to remember this stuff if you actually figure it out yourself. Good luck!
Your code should be (the comments explain my corrections):
def addodds2(x,y):
total=0
if y<len(x): #you don't need a while there
a=x[y] #you have to do this operation if y<len(x), otherwise you would get the index error you are getting
if a%2!=0:
total+=a
return total+addodds2(x,y+1) #you have to sum the current total to the result returned by the addodds2() function (otherwise you would got 0 as the final result)
return total
print(addodds2([3,2,4,7,2,4,1,3,2],0))
while y<len(x)
So the last y which is smaller than len(x) is y = len(x) - 1, so it’s the very last item of the list.
addodds2(x,y+1)
Then you try to access the element after that item, which does not exist, so you get the IndexError.
This code can be very short and elegant:
def add_odds(lst, i=0):
try:
return (lst[i] if lst[i] % 2 == 0 else 0) + add_odds(lst, i+1)
except IndexError:
return 0
Note that, in a truly functional style, you wouldn't keep track of an index either. In Python, it would be rather inefficient, though, but recursion isn't recommended in Python anyway.
def add_odds2(lst):
try:
return (lst[-1] if lst[-1] % 2 == 0 else 0) + add_odds2(lst[:-1])
except IndexError:
return 0
To make it work with any kind of sequence, you can do the following:
def add_odds3(it):
it = iter(it)
try:
value = next(it)
return (value if value % 2 == 0 else 0) + add_odds3(it)
except StopIteration:
return 0
It's much more efficient, though there's not much sense in using an iterator recursively...
I realize that little of this is relevant for your (educational) purposes, but I just wanted to show (all of) you some nice Python. :)

Recursive fibonnaci-like cumulative [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
calling func. change the input
I have to write a recursive function that takes a list of numbers in input and returns a list of numbers in output, for example called like this:
rec_cumsum([2,2,2,3])
the output should be like:
[2,4,6,9]
Thing is, I cant seem to get my head around for this to work.. this got me questioning my whole recursive thinking..
what i have so far is :
newlist = []
k = 1
def rec_cumsum(numbers):
if len(numbers) == 0:
return 0
if len(numbers) > 1 and len(numbers) != (k+1):
newlist[k+1] == newlist[k+1] + newlist[k]
k = k+1
return rec_cumsum(numbers)
but I'm getting errors which doesn't really make any sense to me.
the recursion should always take the number, and add it to the one before it, than save it in the next location of the list.. (new one or original one)
I would write it like this:
def cumulative_sum(lst,prev=0):
if not lst:
return []
else:
elem = prev+lst[0]
return [elem] + cumulative_sum(lst[1:],prev=elem)
print cumulative_sum([2,2,2,3])
Now to look at your code (note I didn't actually work through the logic to decide whether it would give the correct result, the following only addresses possible exceptions that your code is probably throwing):
You're probably getting an IndexError because of this line:
newlist[k+1] == newlist[k+1] + newlist[k]
You're assigning to a list position which doesn't exist yet. You could pre-allocate your list:
newlist = [0]*len(lst)
but even if you fix that, you'll get a recursion error with your code because of the line:
k = k + 1
The problem here is that on the left hand side, k is local whereas on the right hand side, k is global. So essentially each time you run this, you're getting the local k == 2 and not touching the global one. If you really want to modify the global k, you need to declare k as global via global k. Of course, then you need to reset k every time you're going to use this function which would be a somewhat strange API.

Categories

Resources