How to check if tree is symmetric python - python-3.x

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)

Related

Python recursive function to create generic tree [duplicate]

This question already has answers here:
Why does my recursive function return None?
(4 answers)
Closed 7 months ago.
I'm trying to create a tree with n children for each node.
Problem is, when I try to implement this using a recursive function, I ends up with multiple recursive calls so I can't have a single return statement, hence having None as a final result.
Here is my piece of code :
def recursive_func(tree, n):
if n == 0:
return tree
else:
permutations = get_permutations()
for permutation in permutations:
child_tree = recursive_func(Tree(permutation), n-1)
tree.addChild(child_tree)
get_permutations() gives a list of child trees to create. I also have a Tree class with a node value and a list of children.
Here is the Tree class:
class Tree:
def __init__(self, result):
self.node = node
self.children = []
def addChild(self, tree):
self.children.append(tree)
This is probably a rookie error in the design of my problem, but I would be glad to get some help.
TLDR: Since you use the result of recursive_func, it should always return its tree.
The recursive_func has three important points for this problem:
def recursive_func(tree, n):
if n == 0:
return tree # 1.
else:
permutations = get_permutations()
for permutation in permutations:
child_tree = recursive_func(Tree(permutation), n-1) # 2.
tree.addChild(child_tree)
# 3.
Now, 1. defines that the function sometimes returns a Tree. This matches 2. which always expects the function to return a Tree. However, both conflict with 3., which implicitly returns None when the second branch is done.
Since the first branch is empty aside from the return, both 1. and 3. can be collapsed to one path that always returns a Tree.
def recursive_func(tree, n):
if n > 0:
permutations = get_permutations()
for permutation in permutations:
child_tree = recursive_func(Tree(permutation), n-1)
tree.addChild(child_tree)
return tree

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])

AttributeError: 'NoneType' object has no attribute 'left'

Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
class Solution(object):
def hasPathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
l = []
def helper(tree, total):
if not tree.left and not tree.right:
l.append(total)
else:
helper(tree.left, total + tree.val)
helper(tree.right, total + tree.val)
return l
if root:
helper(root, 0)
return sum in l
return False
Can anyone tell me what's wrong with my code???
The primary issue is that your helper improperly handles cases when a node's left or right child are None. It will recurse on both children and crash attempting to access None.left or None.right.
Beyond that, there is no need for a list here; you can return True if you reach a leaf and find that your running total equals the sum. This is more efficient because you avoid traversing the rest of the tree after finding a solution and avoid traversing the list to find your target after the tree search is over.
Also worth mentioning, Leetcode gives the parameter sum, which overwrites a builtin function. I usually rename such variables and took the liberty to do so here.
def hasPathSum(self, root, target):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
def helper(tree, total=0):
if not tree.left and not tree.right:
return target == total + tree.val
if tree.left and helper(tree.left, total + tree.val):
return True
if tree.right and helper(tree.right, total + tree.val):
return True
return False
return helper(root) if root else False

Implement Kahn's topological sorting algorithm using Python

Kahn proposed an algorithm in 62 to topologically sort any DAG (directed acyclic graph), pseudo code copied from Wikipedia:
L ← Empty list that will contain the sorted elements
S ← Set of all nodes with no incoming edges
while S is non-empty do
remove a node n from S
add n to tail of L
for each node m with an edge e from n to m do
remove edge e from the graph # This is a DESTRUCTIVE step!
if m has no other incoming edges then
insert m into S if graph has edges then
return error (graph has at least one cycle) else
return L (a topologically sorted order)
I need to implement it using IPython3, with the following implementation of a DAG:
class Node(object):
def __init__(self, name, parents):
assert isinstance(name, str)
assert all(isinstance(_, RandomVariable) for _ in parents)
self.name, self.parents = name, parents
where name is the label for the node and parents stores all of its parent nodes. Then the DAG class is implemented as:
class DAG(object):
def __init__(self, *nodes):
assert all(isinstance(_, Node) for _ in nodes)
self.nodes = nodes
(The DAG implementation is fixed and not to be improved.) Then I need to implement Kahn's algorithm as a function top_order which takes in a DAG instance and returns an ordering like (node_1, node_2, ..., node_n). The main trouble is, this algorithm is destructive because one of its steps is remove edge e from the graph (line 5) which will delete one member of m.parents. However, I have to leave the DAG instance intact.
One way I can think of so far is to create a deep copy of the DAG instance taken in (even a shallow copy can't do the job because the algorithm still destroys the original instance via references), and perform the destructive algorithm on this copy, and then get the correct ordering of node names of this copy (assume there is no naming conflict between nodes), and then use this ordering of names to infer the correct ordering of the nodes of the original instance, which roughly goes like:
def top_order(network):
'''takes in a DAG, prints and returns a topological ordering.'''
assert type(network) == DAG
temp = copy.deepcopy(network) # to leave the original instance intact
ordering_name = []
roots = [node for node in temp.nodes if not node.parents]
while roots:
n_node = roots[0]
del roots[0]
ordering_name.append(n_node.name)
for m_node in temp.nodes:
if n_node in m_node.parents:
temp_list = list(m_node.parents)
temp_list.remove(n_node)
m_node.parents = tuple(temp_list)
if not m_node.parents:
roots.append(m_node)
print(ordering_name) # print ordering by name
# gets ordering of nodes of the original instance
ordering = []
for name in ordering_name:
for node in network.nodes:
if node.name == name:
ordering.append(node)
return tuple(ordering)
Two problems: first, when network is huge the deep copy will be resource consuming; second, I want an improvement to my nested for loops which gets the ordering of the original instance. (For the second I think something like the sorted method etc pops into my mind.)
Any suggestion?
I'm going to suggest a less literal implementation of the algorithm: you don't need to manipulate the DAG at all, you just need to manipulate info about the DAG. The only "interesting" things the algorithm needs are a mapping from a node to its children (the opposite of what your DAG actually stores), and a count of the number of each node's parents.
These are easy to compute, and dicts can be used to associate this info with node names (assuming all names are distinct - if not, you can invent unique names with a bit more code).
Then this should work:
def topsort(dag):
name2node = {node.name: node for node in dag.nodes}
# map name to number of predecessors (parents)
name2npreds = {}
# map name to list of successors (children)
name2succs = {name: [] for name in name2node}
for node in dag.nodes:
thisname = node.name
name2npreds[thisname] = len(node.parents)
for p in node.parents:
name2succs[p.name].append(thisname)
result = [n for n, npreds in name2npreds.items() if npreds == 0]
for p in result:
for c in name2succs[p]:
npreds = name2npreds[c]
assert npreds
npreds -= 1
name2npreds[c] = npreds
if npreds == 0:
result.append(c)
if len(result) < len(name2node):
raise ValueError("no topsort - cycle")
return tuple(name2node[p] for p in result)
There's one subtle point here: the outer loop appends to result while it's iterating over result. That's intentional. The effect is that every element in result is processed exactly once by the outer loop, regardless of whether an element was in the initial result or added later.
Note that while the input DAG and Nodes are traversed, nothing in them is altered.

Recursion counting leaves in binary trees

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()))

Resources