find a path to a given node in binary tree - Python - python

I'm stuck finding the path of the given node so I can find the right side but not the left side.
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.val = key
def path(self, k):
if not self.val:
return []
if self.val == k:
return [self.val]
res = self.left.path(k)
if res:
return [self.val] + res
res = self.right.path(k)
if res:
return [self.val] + res
return []
For example, when I search for 5 in X="+ * + 5 7 4 1 6", the output is like ['+', '*', '+', '5'] but when I try to search for any number on the lift subtree, it gives me this error:
[Previous line repeated 1 more time]
AttributeError: 'NoneType' object has no attribute 'path'

Before accessing a method like path you should ensure that the object really is a Node instance, and not None.
Not the problem, but not self.val will also be a true expression when that val is 0, which seems like a valid key for a node... I suppose this test is there to detect a node with a None value. Usually such code is used when a tree is always created with at least one node instance, even when it is empty. This is not good practice. An empty tree should not have nodes at all, not even one with a None value.
So correct to:
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.val = key
def path(self, k):
if self.val == k:
return [self.val]
if self.left:
res = self.left.path(k)
if res:
return [self.val] + res
if self.right:
res = self.right.path(k)
if res:
return [self.val] + res
return []

Related

Binary Search Tree Find minimum not clear

The logic I tried:
def min_tree_value(self):
while self.left:
self.left = self.left.left
return self.data
Actual Python program Logic:
def min_tree_value(self):
if self.left is None:
return self.data
return self.left.min_tree_value()
The actual Python program logic is in recursion form. I tried the same logic in While loop()
I'm not sure whether my logic is correct. Do help me to figure out the incorrect logic and point where I'm Wrong.
Your logic is almost there, but not quite:
def min_tree_value(self):
node = self
while node.left:
# don't change the structure by rebinding node.left,
# but iterate the tree by moving along nodes!
node = node.left
return node.data
Note that in the original code, you never reassign self before returning its value, so you always returned the root value.
First of all, the question asks about finding the minimum element in a binary tree.
The algorithm you used, will find the minimum element in the Binary Search Tree (as the leftmost element is the minimum).
For finding minimum element in a simple Binary Tree, use the following algorithm:
# Returns the min value in a binary tree
def find_min_in_BT(root):
if root is None:
return float('inf')
res = root.data
lres = find_min_in_BT(root.leftChild)
rres = find_min_in_BT(root.rightChild)
if lres < res:
res = lres
if rres < res:
res = rres
return res
Additions to the answer after OP changed the question:
The logic for the algorithm you tried is correct, with a small correction in the implementation: self = self.data. Both of them find the leftmost element.
I have also tested both the functions which return the same output:
class Node:
def __init__(self, data):
self.left = None
self.right = None
self.data = data
def insert(self, data):
if self.data:
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.data = data
def findval(self, lkpval):
if lkpval < self.data:
if self.left is None:
return str(lkpval)+" Not Found"
return self.left.findval(lkpval)
elif lkpval > self.data:
if self.right is None:
return str(lkpval)+" Not Found"
return self.right.findval(lkpval)
else:
print(str(self.data) + ' is found')
def PrintTree(self):
if self.left:
self.left.PrintTree()
print( self.data),
if self.right:
self.right.PrintTree()
def min_tree_value_original(self):
if self.left is None:
return self.data
return self.left.min_tree_value_original()
def min_tree_value_custom(self):
while self.left:
self = self.left
return self.data
root = Node(12)
root.insert(6)
root.insert(14)
root.insert(3)
root.insert(3)
root.insert(1)
root.insert(0)
root.insert(-1)
root.insert(-2)
print(root.min_tree_value_original())
print(root.min_tree_value_custom())
Output:
-2
-2
Here -2 is the smallest and the leftmost element in the BST.

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

Leetcode Same Tree

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)

adding an element to binary search tree

Hi my question is how can i fix the code for my add function for my binary search tree program.
class BTNode2:
def __init__(self,d,l,r):
self.data = d
self.left = l
self.right = r
self.mult = 1
And this is the add method
def add(self, d):
if (self < d.data):
if (d.left != None):
add(self, d.left)
else:
d.left = BTNode2(self)
else:
if (d.right != None):
add(self, d.right)
else:
d.right = BTNode2(self)
return
This is the error i get when i try to run the add method:
AttributeError: 'str' object has no attribute 'data'
In essence, you swapped the parameters of the add function, since self is the tree here, and d the data element to add. Furthermore in order to construct such BTNode2s with only one parameter, you should add a default value for l and r. Finally depending on what mult does, you might want to change this in the add algorithm, but it is not really clear what it represents.
So we can fix this to:
class BTNode2:
def __init__(self, d, l=None, r=None):
self.data = d
self.left = l
self.right = r
self.mult = 1
def add(self, d):
if d < self.data:
if self.left is not None:
self.left.add(d)
else:
self.left = BTNode2(d)
else:
if self.right is not None:
self.right.add(d)
else:
self.right = BTNode2(d)

Delete min binary search tree Python

I am trying to delete the minimum node from a BST, so I search through the tree until I get the min (when root.leftnode is None) and then set root.rightnode to the root itself to continue the BST.
The issue is when I check the tree after doing this it does not show the deletion ever occurred.
Could someone point me in the right direction please, any advice is appreciated.
class node():
def __init__(self, key, data):
self.data = data
self.key = key
self.leftnode = None
self.rightnode = None
self.count = 1
class binarysearch():
def __init__(self):
self.size = 0
self.rootnode = None
def insert(self, key, data):
if self.rootnode is None:
self.rootnode = node(key, data)
else:
self.insertnode(self.rootnode, key, data)
def getroot(self):
return self.rootnode
def insertnode(self, root, key, data):
if root.key == key:
root.data = data
elif key < root.key:
if root.leftnode is None:
root.leftnode = node(key, data)
else:
self.insertnode(root.leftnode, key, data)
else:
if root.rightnode is None:
root.rightnode = node(key, data)
else:
self.insertnode(root.rightnode, key, data)
root.count = 1 + self.sizenode(root.leftnode) + self.sizenode(root.rightnode)
def inorder(self, root):
if root is not None:
self.inorder(root.leftnode)
print(root.key)
self.inorder(root.rightnode)
def deletemin(self):
if self.rootnode is None:
print("No nodes exist")
else:
self.deleteminnode(self.rootnode.leftnode)
def deleteminnode(self, root):
if root.leftnode is not None:
self.deleteminnode(root.leftnode)
else:
print (root.key, "deleted")
root = root.rightnode
if __name__ == '__main__':
a = binarysearch()
a.insert(7,7)
a.insert(1,1)
a.insert(8,8)
a.insert(3,3)
a.insert(9,9)
a.insert(2,2)
a.insert(4,4)
a.insert(11,11)
a.insert(10,10)
a.deletemin()
a.getnodes()
The issue you have is that root = root.rightnode only rebinds the local variable root. It doesn't change the other places you have references to that node (such as its parent in the tree).
To fix this, you need to change how your recursive function works. Rather than expecting it to do all the work in the last call, it should instead return the value that should be the left node of its parent. Of then that will be the node itself, but for the minimum node, it will be its right child instead.
def deletemin(self):
if self.rootnode is None:
print("No nodes exist")
else:
self.rootnode = self.deleteminnode(self.rootnode)
def deleteminnode(self, root):
if root.leftnode is not None:
root.leftnode = self.deleteminnode(root.leftnode)
return root
else:
return root.rightnode
A final note regarding names: It's a bit weird to use root as the name of a random node within the tree. Usually a tree has just the one root node, and others nodes aren't called root since they have parents. Unfortunately, the most conventional name node is already being used for your node class. Normally classes should be given CapitalizedNames, so that lowercase_names can exclusively refer to instances and other variables. This is just convention though (and builtin types like list break the rules). It might be easier for others to understand your code if you use standard name styles, but Python doesn't enforce them. It will allow you to use whatever names you want. Even the name self is not a requirement, though it would be very confusing if you used something different for the first argument of a method without a good reason (an example of a good reason: classmethods and methods of metaclasses often use cls as the name of their first arguments, since the object will be a class).
You can find all the nodes in the tree, along with the path to the node, find the minimum of the results, and then traverse the generated path to delete the node:
class Tree:
def __init__(self, **kwargs):
self.__dict__ = {i:kwargs.get(i) for i in ['val', 'left', 'right']}
def get_nodes(self, current = []):
yield [''.join(current), self.val]
yield from getattr(self.right, 'get_nodes', lambda _:[])(current+['1'])
yield from getattr(self.left, 'get_nodes', lambda _:[])(current+['0'])
def __iter__(self):
yield self.val
yield from [[], self.left][bool(self.left)]
yield from [[], self.right][bool(self.right)]
def _insert_back(self, _v):
if not self.val:
self.val = _v
else:
if _v < self.val:
getattr(self.left, '_insert_back', lambda x:setattr(x, 'left', Tree(val=x)))(_v)
else:
getattr(self.right, '_insert_back', lambda x:setattr(x, 'right', Tree(val=x)))(_v)
def remove(self, _path, _to_val, last=None):
'''_to_val: if _to_val is None, then the item is removed. If not, the node value is set to _to_val'''
if _path:
getattr(self, ['left', 'right'][int(_path[0])]).remove(_path[1:], _to_val, last = self)
else:
if _to_val is None:
last.left = None
last.right = None
for i in [[], self.left][bool(self.left)]:
last._insert_back(i)
for i in [[], self.right][bool(self.right)]:
last._insert_back(i)
else:
self.val = _to_val
Creating:
7
5 9
4 6 8 10
12
t = Tree(val = 7, left=Tree(val = 5, left=Tree(val=4), right=Tree(val=6)), right=Tree(val=9, left=Tree(val=8), right=Tree(val=10, right=Tree(val=12))))
path, _to_remove = min(t.get_nodes(), key=lambda x:x[-1])
print(f'Removing {_to_remove}')
t.remove(path, None)
print([i for i in t])
Output:
4
[7, 5, 9, 8, 10, 12]

Categories

Resources