Recursion counting leaves in binary trees - python-3.x

I don't understand, why i am so incapable of coming up with a simple recursive function to count the number of leaves on a binary tree. I am ashamed to say, I've just been blank and staring at the monitor trying to figure out why everything doesn't work.
Why is it so difficult to realize, what works and what doesn't? Is this supposed to be intuitive or unintuitive?
def num_leaves(tree):
if (tree.get_left() is None) and (tree.get_right is None()):
return 1
#something goes here..
return #something here..
Can someone offer some advice why I may not finding this as simple as its suppose to be
Thanks

I suspect that this is one of the first few times you've dealt with recursion. I say this, because I felt the same way, my first few times.
You have the correct idea to start with: you've identified and (correctly) implemented your base case. So, let's write down some thoughts about the solution before we actually code it up:
Basic Idea:
Identify all the leaves of a BTree
If a node has no children, it is a leaf
If a node does have children, it is not a leaf. Count the number of leaves on the left BTree and the number of nodes on the right BTree and add them up
Ok now that we have the basic idea, let's try some pseudocode:
function countLeaves(rootNode):
if nootNode has no children: # this is a leaf
return 1
if rootNode has a left child: # non-leaf nodes may be missing a left or right child
leftLeaves = countLeaves(left child of rootNode)
else:
leftLeaves = 0
if rootNode has a right child:
rightLeaves = countLeaves(right child of rootNode)
else:
rightLeaves = 0
return leftLeaves + rightLeaves
Make sense? Ok, let's write some python code:
def countLeaves(root):
if (root.get_left() is None) and (root.get_right() is None): # we are a leaf
return 1
leftChild = root.get_left()
if leftChild is not None:
leftChildren = countLeaves(leftChild)
else:
leftChildren = 0
rightChild = root.get_right()
if rightChild is not None:
rightChildren = countLeaves(rightChild)
else:
rightChildren = 0
return leftChildren + rightChildren
Now that we have that idea coded up, let's clean it up a bit:
def countLeaves(root):
if root.get_left() is None and root.get_right() is None: return 1
return (0 if root.get_left() is None else countLeaves(root.get_left())) + (0 if root.get_right() is None else countLeaves(root.get_right()))

Related

Comparing whether two trees are same or not? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 12 months ago.
Improve this question
This is the question
https://leetcode.com/problems/same-tree/
Question:
Given the roots of two binary trees p and q, write a function to check if they are the same or not.
Two binary trees are considered the same if they are structurally identical, and the nodes have the same value.
I have used Python for solving this problem
This is my approach. Could you tell why it failed the given test case?
https://leetcode.com/submissions/detail/661792014/
My approach:
#Definition for a binary tree node.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
a = m = p
b = n = q
if p is None and q is None:
return True
elif p is None or q is None:
return False
while a:
if a.val != b.val:
return False
if a.left:
a = a.left
if b.left:
b = b.left
else:
return False
else:
if b.left:
return False
else:
break
while m:
if m.val != n.val:
return False
if m.right:
m = m.right
if n.right:
n = n.right
else:
return False
else:
if n.right:
return False
else:
break
return True
The problem is that in your while loop, m will go all the way down via the same-side child (first loop: left, second loop right), but when it reaches the end, it will not backtrack and then look at all the opposite sided subtrees it had skipped along the way.
For example, take this tree:
4
/ \
2 6
/ \ / \
1 3 5 7
The first loop will check the left side (i.e. 4, 2 and 1), and the second loop will check the right side (i.e. 4, 6, 7), but there is no provision to go down the tree in a zig-zag way, so the nodes with 3 and 5 are never checked.
What you need here, is recursion. Solve the problem recursively for the left subtree and again recursively for the right subtree. If either returns that there is a difference, stop, and return False. If both calls give a positive result, and also the current node is equal, return True.
The solution (spoiler):
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
return bool(not p and not q or
p and q and p.val == q.val
and self.isSameTree(p.left, q.left)
and self.isSameTree(p.right, q.right))

Original linked list and its copy, both are getting updated simultaneously when I am making changes only to one?

I am trying to solve LeetCode problem 19. Remove Nth Node From End of List:
Given the head of a linked list, remove the nth node from the end of the list and return its head.
Here is my solution.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
Length=1
curr=head
while curr.next!=None:
Length=Length+1
curr=curr.next
count = 0
curr=head
if Length == n:
return head.next
while count < Length-n:
if count == Length-n-1:
curr.next = curr.next.next
break
count = count+1
curr = curr.next
return head
My question is, when I am making all the changes to curr (copy of the original), why are those changes getting reflected in the head (original) as well?
The thing is that curr is not a copy of the original (node or list). It is a name you give for a given node that is in the original list. As long as you don't really create new nodes (calling Node(value)), you cannot be referencing anything else than the nodes in the original list. curr references one node after the other in the original list and at a certain point mutates a next reference. This happens in the original list. There is no other list at play.
The only time that the caller does not see a change in the original list, is when you don't return head, but head.next. In that case head still refers to the original list, while the returned node reference, is skipping the first node. But both lists have all nodes in common except the node referenced by head.

Eulclid's Algorithm : Python code returns None at the end of recursion [duplicate]

This question already has answers here:
Why does my recursive function return None?
(4 answers)
Closed 3 years ago.
I am trying to implement Euclid's algorithm for computing the greatest common divisor using recursion. Below is the code for the same.
def euclid(a,b):
if a>b:
r=a%b
if (r == 0):
return b
else:
a=b
b=r
euclid(a,b)
print(euclid(20,4)) # Returns 4
print(euclid(20,8)) # Returns None
For the first data set, I get the correct result. But for euclid(20,8) I get a return of None.
While checking in the debugger, I did see the return value of b become 4 but then for some reason, my code jumps to euclid(a,b) and returns None.
The major takeaway here would be to understand why the code does not return 4 but jumps to the euclid(a,b) and return None.
Please refrain from giving alternative code solutions but you are very much encouraged to point out the reason for the current behaviour of the code.
The reason for that code to return None at some point is that in your control flow you eventually end up in a situation where there is no return statement, e.g. for a <= b in your first if or when r != 0 in your second if. The default behavior of Python in that case is to return None, as you seems to have discovered the hard way.
def euclid(a,b):
if a>b:
r=a%b
if (r == 0):
return b
else: # <--- no `return` in this branch!
a=b
b=r
euclid(a,b)
# else: # <--- no `return` in this branch!
# ...
Here is an updated version of your code, that addresses that and also a number of other issues:
the name of the function should be meaningful: Euclid is kind of popular and there is a bunch of things named after him, better specify that you actually want to compute the greatest common divisor (GCD)
the first if is not really needed, as it is taken care of by the subsequent code: computing r and the recursive call will take care of swapping a and b if a < b, and if a == b then r == 0, so you b is being returned directly
a = b and b = r are useless, do not use them, just have your recursive call use b and r directly
the second if can be written more clearly in one line
def gcd_euclid_recursive(a, b):
r = a % b
return gcd_euclid_recursive(b, r) if r else b
gcd_euclid_recursive(120, 72)
# 24
gcd_euclid_recursive(64, 120)
# 8
You don't actually return anything in the else path so it just assumes you know best and returns None for you.
The only reason you get 4 for print(euclid(20,4)) is because 4 is a factor of 20 so never uses the else path. Instead it returns b immediately. For anything else, the thing returned from the first recursive call to euclid() will always be None (even if a lower call to that function returns something, you throw it away when returning from the first call).
You need return euclid(a,b) rather than just euclid(a,b).
As an aside (this isn't necessary to answer your specific question but it goes a long way toward improving the implementation of the code), I'm not a big fan of the if something then return else … construct since the else is totally superfluous (if it didn't return, the else bit is automatic).
Additionally, you don't need to assign variables when you can just change what gets passed to the next recursive level.
Taking both those into account (and simplifying quite a bit), you can do something like:
def euclid(a, b):
if b == 0: return a
return euclid(b, a % b)
Your code has an indentation error and one path is missing in your code(r!=0). It should be
def euclid(a,b):
if a>=b:
r=a%b
if (r == 0):
return b
return euclid(b, r)
else:
return euclid(b, a)

Generate all valid binary search trees given a list of values

Hello I am trying to solve the following question on leetcode, [https://leetcode.com/problems/unique-binary-search-trees-ii/].
I know I have access to the solution but I tried solving the problem my way and I am stuck and I would like to know if it is solvable the way I am doing it.
Here is the code:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def generateTrees(myrange, n, res = None):
if res == None:
res = []
if myrange == []:
res.append(None)
return
for root in myrange:
res.append(root)
generateTrees([i for i in range(root) if i in set(myrange)], n, res) #leftchild
generateTrees([i for i in range(root+1, n) if i in set(myrange)], n, res) #rightchild
return res
Initially myrange is just the list containing the node values, and n is the length of myrange.
The way I am doing it is a sort of DFS where I loop over the nodes making each one of them the root once and then I do the same for the left and right subtrees to get all combinations. But the problem I am facing is I can't figure out how to manage res to remove elements from it as my recursion backtracks (and make it so res only contains valid bst's and then put those in some other list that will be my actual result).
I would like some pointers or even just comments on if you think my approach is valid or bad ..etc.
Issues:
As you mention, your code only creates one list to which it keeps appending.
Even if you would fix that, the lists would never come out in the BFS kind of order, which is what the question's example seems to suggest.
For a chosen root, you need to list all combinations of its possible left subtrees with its possible right subtrees -- a Cartesian product if you wish. This logic is missing in your code.
I would:
not pass res as argument to the recursive function. Just return it, and let the caller deal with it.
not use ranges, as that only seems to complicate things. The if i in set(myrange) seems like an inefficient way to get the overlap between two ranges. I would instead pass the two extremes of the range as separate arguments.
use the TreeNode class to actually create the trees, and deal with generating the required output format later.
For generating the output format you need a BFS walk through the tree, and this could be implemented as a method on TreeNode.
Here is what I think would work:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def breadth_first(self):
lst = []
todo = [self]
while any(todo):
node = todo.pop(0)
lst.append(node.val if node else None)
if node:
todo.append(node.left)
todo.append(node.right)
return lst
def generateTrees(n):
def recur(start, end): # end is not included
if start >= end:
return [None]
trees = []
for root in range(start, end):
lefts = recur(start, root)
rights = recur(root+1, end)
# Cartesian product:
for left in lefts:
for right in rights:
# Start with a new tree, and append to result
tree = TreeNode(root)
tree.left = left
tree.right = right
trees.append(tree)
return trees
return recur(1, n+1)
# Create the trees as a list of TreeNode instances:
trees = generateTrees(3)
# Convert to a list of lists
print([tree.breadth_first() for tree in trees])

How to check if tree is symmetric python

For practice, I solved Leetcode 101. Symmetric Tree question:
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
I have an idea to do in order traversal, record each node value into list and check the value from the first part, and reverse the second part from the list. But
it failed on test case [1,2,3,3,null,2,null]
from my local, my value return [3, 2, None, 1, 2, 3, None], but from leetcode it return [3,2,1,2,3] anyone know why and anything wrong with my code?
def isSymmetric(root: 'TreeNode') -> 'bool':
if not root: return True
value = []
def traversal(cur):
if cur:
traversal(cur.left)
value.append(cur.val)
traversal(cur.right)
traversal(root)
size = int(len(value) / 2)
return value[:size] == value[size + 1:][-1::-1]
I'm afraid inorder traversal does not uniquely determine a tree. e.g. a tree with structure
1
\
2
\
3
has the same inorder traversal as
2
/ \
1 3
Since you have the if cur condition, your inorder traversal won't include null nodes, which makes the traversal non-unique. You can include the null nodes like this:
def traverse(cur):
if cur:
traverse(cur.left)
values.append(cur.val if cur else None)
if cur:
traverse(cur.right)
This would serialize the tree nodes uniquely.
What you can also do is in this case determine the structure of the left node and right node are the same (except left and right are reversed). Here is my accepted solution:
class Solution:
def isSymmetric(self, root: 'TreeNode') -> 'bool':
if not root:
return True
return self.isSymmetricHelper(root.left, root.right)
def isSymmetricHelper(self, node1, node2):
if node1 is None and node2 is None:
return True
if node1 is None or node2 is None:
return False
if node1.val != node2.val: # early stopping - two nodes have different value
return False
out = True
out = out and self.isSymmetricHelper(node1.left, node2.right)
if not out: # early stopping
return False
out = out and self.isSymmetricHelper(node1.right, node2.left)
return out
It recursively check if two trees are the mirror of each other (with some early stopping). The idea is if two trees are mirror, the left subtree of tree1 must be the mirror of the right subtree of tree2, same applies for right subtree of tree1 and left subtree of tree2.
Although the runtime of both are O(n), he recursive method takes O(logn) average space (used by call stack) and has early stopping built-in, while your serialize-all-nodes method takes O(n) space O(n) time.
this is how the symmetric tree is:
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
def is_mirror(t1,t2):
# If I reached all the way down that means I always got True.
if t1 is None and t2 is None:
return True
# if one of them is None but other one is not then False
if t1 is None or t2 is None:
return False
# 2=2 and t1.left==t2.right and t1.right==t2.left
return t1.val==t2.val and is_mirror(t1.left,t2.right) and is_mirror(t1.right,t2.left)
return is_mirror(root.left,root.right)

Resources