Leetcode Same Tree - python

I'm trying to solve same tree from leetcode with python.
original problem. https://leetcode.com/problems/same-tree/
My code was able to pass a few test cases but not all. It couldn't pass the submission. My idea is to flatten the tree and compare the two lists. The failed case is at the bottom of the code.
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def isSameTree(self, p, q):
"""
:type p: TreeNode
:type q: TreeNode
:rtype: bool
"""
def flatten(root):
if root is None:
return ["null"]
if root.left is None and root.right is None:
return [root.val]
if root.left is None:
# print 'left', [root.val, "null", flatten(root.right)]
return [root.val, "null", flatten(root.right)]
if root.right is None:
# print 'right', [root.val, flatten(root.left), "null"]
return [root.val, flatten(root.left), "null"]
else:
# print flatten(root.right) + flatten(root.right)
return flatten(root.right) + flatten(root.right)
return flatten(p) == flatten(q)
## Failed test case
## [390,255,2266,-273,337,1105,3440,-425,4113,null,null,600,1355,3241,4731,-488,-367,16,null,565,780,1311,1755,3075,3392,4725,4817,null,null,null,null,-187,152,395,null,728,977,1270,null,1611,1786,2991,3175,3286,null,164,null,null,4864,-252,-95,82,null,391,469,638,769,862,1045,1138,null,1460,1663,null,1838,2891,null,null,null,null,3296,3670,4381,null,4905,null,null,null,-58,null,null,null,null,null,null,null,null,734,null,843,958,null,null,null,1163,1445,1533,null,null,null,2111,2792,null,null,null,3493,3933,4302,4488,null,null,null,null,null,null,819,null,null,null,null,1216,null,null,1522,null,1889,2238,2558,2832,null,3519,3848,4090,4165,null,4404,4630,null,null,null,null,null,null,1885,2018,2199,null,2364,2678,null,null,null,3618,3751,null,4006,null,null,4246,null,null,4554,null,null,null,1936,null,null,null,null,2444,2642,2732,null,null,null,null,null,null,null,4253,null,null,null,null,2393,2461,null,null,null,null,4250,null,null,null,null,2537]
## [390,255,2266,-273,337,1105,3440,-425,4113,null,null,600,1355,3241,4731,-488,-367,16,null,565,780,1311,1755,3075,3392,4725,4817,null,null,null,null,-187,152,395,null,728,977,1270,null,1611,1786,2991,3175,3286,null,164,null,null,4864,-252,-95,82,null,391,469,638,769,862,1045,1138,null,1460,1663,null,1838,2891,null,null,null,null,3296,3670,4381,null,4905,null,null,null,-58,null,null,null,null,null,null,null,null,734,null,843,958,null,null,null,1163,1445,1533,null,null,null,2111,2792,null,null,null,3493,3933,4302,4488,null,null,null,null,null,null,819,null,null,null,null,1216,null,null,1522,null,1889,2238,2558,2832,null,3519,3848,4090,4165,null,4404,4630,null,null,null,null,null,null,1885,2018,2199,null,2364,2678,null,null,null,3618,3751,null,4006,null,null,4246,null,null,4554,null,null,null,1936,null,null,null,null,2444,2642,2732,null,null,null,null,null,null,null,4253,null,null,null,null,2461,2393,null,null,null,null,4250,null,null,null,null,2537]

To determine if two trees are identical, you need to determine that the structure is the same i.e equal nodes at the same level and branch. You can write a recursive function that loops over the tree and creates a Huffman encoding, which can later be used to compare the nodes:
class Solution(object):
#classmethod
def tree_paths(cls, _tree:TreeNode, _paths = []):
yield [_tree.val, _paths]
if _tree.left is not None:
yield from cls.tree_paths(_tree.left, _paths+[0])
if _tree.right is not None:
yield from cls.tree_paths(_tree.right, _paths+[1])
def isSameTree(self, p, q):
if p is None and q is None:
return True
if not all([p, q]):
return False
_tree1, _tree2 = list(self.__class__.tree_paths(p)), list(self.__class__.tree_paths(q))
if len(_tree1) != len(_tree2):
return False
return all(a == b and all(c ==d for c, d in zip(j, l)) for [a, j], [b, l] in zip(_tree1, _tree2))
To test, a tree with the same attributes as the original TreeNode can be created:
class TreeNode:
def __init__(self, **kwargs:dict) -> None:
self.__dict__ = {i:kwargs.get(i) for i in ['val', 'left', 'right']}
t1 = TreeNode(val=1, left=TreeNode(val=2), right=TreeNode(val=3))
t2 = TreeNode(val=1, left=TreeNode(val=2), right=TreeNode(val=3))
t3 = TreeNode(val=1, left=TreeNode(val=2), right=TreeNode(val=4))
print(Solution().isSameTree(t1, t2))
print(Solution().isSameTree(t1, t3))
Output:
True
False
This answer was successfully accepted on LeetCode.
Edit: Your code is close, however, flatten(root.right) + flatten(root.right) must be changed, as you are finding the path of nodes on the right subtree twice. Also, you have to include the value of the current tree instance passed to isSameTree. Thus, flatten(root.right) + flatten(root.right) must become:
return flatten(root.left)+[root.val]+flatten(root.right)

Related

How to find duplicate subtrees

I am having trouble find the duplicate subtrees.
In the following leetcode problem 652. Find Duplicate Subtrees
Given the root of a binary tree, return all duplicate subtrees. For each kind of duplicate subtrees, you only need to return the root node of any one of them. Two trees are duplicate if they have the same structure with the same node values.
My implementation is as follows:
# 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 __init__(self):
self.duplicates = []
def isSameSubtree(self, left, right):
if left is None or right is None:
return True
elif left is None:
return False
elif right is None:
return False
if left.val != right.val:
return False
return self.isSameSubtree(left.left, right.left) or \
self.isSameSubtree(left.right, right.right)
def helper(self, node):
if node is None:
return node
left = self.helper(node.left)
right = self.helper(node.right)
if self.isSameSubtree(left, right):
self.duplicates.append(left)
return left
def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]:
self.helper(root)
return self.duplicates
My thought process was that I would check both left and right subtrees by passing each parent node of each subtree into isSameSubtree method. Problem is I don't feel like I am doing any of this correctly. What I want is to analyze both left and right subtrees by doing what I am doing in my helper function and when I go back up the stack and find a parent node with both left and right subtrees that match return that parent node.
Here is a solution that is related to Merkle Trees. In fact, we can define a helper function (no need to modify the Node or Tree class, although of course that is also a possibility if preferred) as follow:
def sign(t):
"""depth-first iterator; for each node, yield (hash, node) tuple"""
hashes = (hash(t.data), )
for c in t.children or []:
for h, n in sign(c):
yield h, n
hashes = hashes + (h, )
yield hash(hashes), t
Usage example, on a simplistic Node structure:
class Node:
def __init__(self, data, children=None):
self.data = data
self.children = children
def __repr__(self):
if self.children:
return f'{self.data}:{self.children}'
return str(self.data)
Then, for example:
t = Node('a', (Node('b', (Node('c'), Node('d'))), (Node('b', (Node('c'), Node('d'))))))
>>> t
a:(b:(c, d), b:(c, d))
>>> list(sign(t))
[(-8022040514780200320, c),
(-5838450013443269355, d),
(-6612941109014678970, b:(c, d)),
(-8022040514780200320, c),
(-5838450013443269355, d),
(-6612941109014678970, b:(c, d)),
(-3704690573080571573, a:(b:(c, d), b:(c, d)))]
As you can see, the subtrees Node('d') all have the same fingerprint: -5838450013443269355, and all b:(c, d) nodes have -6612941109014678970 (Note: actual numerical values will depend on your PYTHONHASHSEED, the values given here are just examples). One very nice property of this is to be very lightweight, it is O[n] for n nodes (there are no needlessly repeated hash computations).
From there, it is trivial to detect repeated subtrees:
seen = set()
for h, c in sign(t):
if h in seen:
print(f'duplicate: {c}')
seen.add(h)
# on the example tree above, this prints:
# duplicate: c
# duplicate: d
# duplicate: b:(c, d)
Note, if you are allowed to modify your Node structure, then the solution is even simpler: add this to your Node structure:
def __hash__(self):
return hash((self.data, tuple(hash(c) for c in self.children or [])))
def __eq__(self, other):
return isinstance(other, Node) and self.data == other.data and self.children == other.children
You can then compute the hash of any Node and compare for equality between nodes that have the same hash. In practice, your duplicate detection becomes:
def dupes(t, seen=None):
if seen is None:
seen = set()
for c in t.children or []:
yield from dupes(c, seen)
if t in seen:
yield t
else:
seen.add(t)
Example:
>>> list(dupes(t))
[c, d, b:(c, d)]

How can I find the depth of a specific node inside a binary tree?

I'm trying to figure out a recursive solution to this problem. The main thing is to return the level in the binary tree where the node is.
def find_depth(tree, node):
if node == None:
return 0
else:
return max(find_depth(tree.left))
#recursive solution here
Using this class for the values:
class Tree:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
Example: Calling find_depth(tree, 7) should return the level where 7 is in the tree. (level 2)
3
/ \
7 1 <------ return that 7 is at level 2
/ \
9 3
maybe this is what you are looking for
def find_depth(tree, node):
if node is None or tree is None:
return 0
if tree == node:
return 1
left = find_depth(tree.left, node)
if left != 0:
return 1 + left
right = find_depth(tree.right, node)
if right != 0:
return 1 + right
return 0
You need to provide information about depth in find_depth call. It might look like this (assuming 0 is a sentinel informing that node is not found):
def find_depth(tree, node, depth=1):
if node == None:
return 0
if tree.value == node:
return depth
left_depth = find_depth(tree.left, node, depth+1)
right_depth = find_depth(tree.right, node, depth+1)
return max(left_depth, right_depth)
Then you call it with two parameters: x = find_depth(tree, 7).
Recursion is a functional heritage and so using it with functional style yields the best results -
base case: if the input tree is empty, we cannot search, return None result
inductive, the input tree is not empty. if tree.data matches the search value, return the current depth, d
inductive, the input tree is not empty and tree.data does not match. return the recursive result of tree.left or the recursive result of find.right
def find (t = None, value = None, d = 1):
if not t:
return None # 1
elif t.value == value:
return d # 2
else:
return find(t.left, value, d + 1) or find(t.right, value, d + 1) # 3
class tree:
def __init__(self, value, left = None, right = None):
self.value = value
self.left = left
self.right = right
t = tree \
( 3
, tree(7, tree(9), tree(3))
, tree(1)
)
print(find(t, 7)) # 2
print(find(t, 99)) # None
You can implement the find method in your tree class too
def find (t = None, value = None, d = 1):
# ...
class tree
def __init__ #...
def find(self, value)
return find(self, value)
print(t.find(7)) # 2
print(t.find(99)) # None

Generate valid binary search tree with Python hypothesis by paramertizing recursive calls

How do you parametrize recursive strategies in the Python hypothesis library?
I'd like to test that the is_valid_bst function works by generating valid BSTs with a recursive strategy.
import hypothesis as hp
from hypothesis import strategies as hps
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def __repr__(self):
if not self.left and not self.right:
return f'TreeNode({self.val})'
return f'TreeNode({self.val}, left={self.left}, right={self.right}'
def is_valid_bst(node):
if not node:
return True
is_valid = True
if node.left:
is_valid = is_valid and node.val > node.left.val
if node.right:
is_valid = is_valid and node.val < node.right.val
if not is_valid:
return False
return is_valid_bst(node.left) and is_valid_bst(node.right)
#hps.composite
def valid_bst_trees(draw, strategy=None, min_value=None, max_value=None):
val = draw(hps.integers(min_value=min_value, max_value=max_value))
node = TreeNode(val)
node.left = draw(strategy)
node.right = draw(strategy)
return node
def gen_bst(tree_strategy, min_value=None, max_value=None):
return hps.integers(min_value=min_value, max_value=max_value).flatmap(
lambda val: valid_bst_trees(
strategy=tree_strategy, min_value=min_value, max_value=max_value))
#hp.given(hps.recursive(hps.just(None), gen_bst))
def test_is_valid_bst_works(node):
assert is_valid_bst(node)
I figured it out. My main misunderstanding was:
The tree_strategy created by the hypothesis.recursive strategy is safe to draw from multiple times and will generate appropriate recursion.
A few other gotchas:
The base case needs both None and a singleton tree. With only None, you'll only generate None.
For the singleton tree, you must generate a new tree every time. Otherwise, you'll end up with cycles in the tree since each node is the same tree. Easiest way to accomplish this is hps.just(-111).map(TreeNode).
You'll need to overwrite the base case if it's a singleton tree to respect min_value and max_value.
Full working solution:
import hypothesis as hp
from hypothesis import strategies as hps
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def __repr__(self):
if not self.left and not self.right:
return f'TreeNode({self.val})'
return f'TreeNode({self.val}, left={self.left}, right={self.right}'
def is_valid_bst(node):
if not node:
return True
is_valid = True
if node.left:
is_valid = is_valid and node.val > node.left.val
if node.right:
is_valid = is_valid and node.val < node.right.val
if not is_valid:
return False
return is_valid_bst(node.left) and is_valid_bst(node.right)
#hps.composite
def valid_bst_trees(
draw, tree_strategy, min_value=None, max_value=None):
"""Returns a valid BST.
Idea is to pick an integer VAL in [min_value, max_value) for this tree and
and use it as a constraint for the children by parameterizing
`tree_strategy` so that:
1. The left child value is in [min_value, VAL).
2. The right child value is in (VAL, min_value].
"""
# We're drawing either a None or a singleton tree.
node = draw(tree_strategy)
if not node:
return None
# Can't use implicit boolean because the values might be falsey, e.g. 0.
if min_value is not None and max_value is not None and min_value >= max_value:
return None
# Overwrite singleton tree.val with one that respects min and max value.
val = draw(hps.integers(min_value=min_value, max_value=max_value))
node.val = val
node.left = draw(valid_bst_trees(
tree_strategy=tree_strategy,
min_value=min_value,
max_value=node.val - 1))
node.right = draw(valid_bst_trees(
tree_strategy=tree_strategy,
min_value=node.val + 1,
max_value=max_value))
return node
def gen_bst(tree_strategy, min_value=None, max_value=None):
return valid_bst_trees(
tree_strategy=tree_strategy,
min_value=min_value,
max_value=max_value)
# Return a new, distinct tree node every time to avoid self referential trees.
singleton_tree = hps.just(-111).map(TreeNode)
#hp.given(hps.recursive(hps.just(None) | singleton_tree, gen_bst))
def test_is_valid_bst_works(node):
assert is_valid_bst(node)
# Simple tests to demonstrate how the TreeNode works
def test_is_valid_bst():
assert is_valid_bst(None)
assert is_valid_bst(TreeNode(1))
node1 = TreeNode(1)
node1.left = TreeNode(0)
assert is_valid_bst(node1)
node2 = TreeNode(1)
node2.left = TreeNode(1)
assert not is_valid_bst(node2)
node3 = TreeNode(1)
node3.left = TreeNode(0)
node3.right = TreeNode(1)
assert not is_valid_bst(node3)

Python find lowest common ancestor of two nodes in a binary tree if not all of these nodes in the tree

I understand how to solve the question if these two nodes must in the Binary Tree, but what if they do not have to be in the tree? If only one or none of these nodes in the tree, return None.
Here is my code:
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
[root,count] = self.findAncestor(root,p,q,0)
if count == 2:
return root
else:
return None
def findAncestor(self,root,p,q,count):
if not root:
return None, count
left,left_count = self.findAncestor(root.left, p, q,count)
right,right_count = self.findAncestor(root.right,p,q,count)
if root == p or root == q:
return root,count+1
if left and right:
return root,count
elif left:
return left,left_count
else:
return right,right_count
but I keep getting incorrect answer. Anyone know how to fix it based on my code?
thanks!
We can count target node number and if it's 2, then we know both nodes are in the tree.
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
self.count = 0
node = self.find(root, p, q)
return node if self.count == 2 else None
def find(self, node, p, q):
if not node:
return None
if node in (p, q):
self.count += 1
left = self.find(node.left, p, q)
right = self.find(node.right, p, q)
return node if node in (p, q) or left and right else left or right
Base on kitt's solution, I test his solution on lintCode problem 578, but it did not passed. The issue happens at counting condition, which should check one more time with those input two node. So I redesign a new solution which passed lintcode test, also with better reading logic.
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
this.val = val
this.left, this.right = None, None
"""
class Solution:
"""
#param: root: The root of the binary tree.
#param: A: A TreeNode
#param: B: A TreeNode
#return: Return the LCA of the two nodes.
"""
count = 0
def lowestCommonAncestor3(self, root, A, B):
result = self.lca(root, A, B)
return result if self.count == 2 else None
def lca(self, root, A, B):
if not root:
return None
for node in [A, B]:
if root == node:
self.count += 1
left = self.lca(root.left, A, B)
right = self.lca(root.right, A, B)
if root in (A, B) or left and right:
return root
if left:
return left
if right:
return right
return None

Depth first search not returning correct object type

I have the following code that traverses a tree object, but I can't get it to return the node object when the key is found. it gives me NoneType instead. the objects are simple and included below.
class StructureTree:
def __init__(self, issueID, depth):
self.issueID = issueID
self.depth = depth
self.children = []
self.worklog_data_rows = []
self.structure_data_row = [] #contains issue data for any issue found in the global structure
def addChild(self, elem):
self.children += [elem]
def __repr__(self):
return "<%d : %d>" % (self.issueID, self.depth)
class StructureForest:
def __init__(self):
self.trees = []
self.roots =[]
def addRoot(self, root):
self.roots += [root]
def DFS(node, key):
'''
Depth First Traversal (In-order).
#node -- int
#key -- int
'''
if node.issueID == key:
print "Found!"
return node
else:
for child in node.children:
print "here"
return DFS(child, key)
def search_node(n_tree_forest, key):
'''
Traverses entire forest.
#key -- int
'''
for root in n_tree_forest.roots:
val = DFS(root, key)
return val
You never return a value either in the main function or in any of the recursive steps. Both times you call DFS you need to do return DFS(..., ...).
Hi I think I see the issue doesn't look like you are returning anything when you go into the else statement which you have to do for proper recursion. Modify your DFS method...
def DFS(node, key):
if node.issueID == key:
print "Found!"
return node.issueID
else:
for child in node.children:
print "here"
val = DFS(child, key)
if val: return val
return False

Categories

Resources