How to search in a tree? - python

I have this follow tree implementation:
class Node:
def __init__(self, *, val):
self.val = val
def __repr__(self):
return "{}(val={})".format(type(self).__name__, self.val)
class Tree:
def __init__(self, *, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def __repr__(self):
return "{}(val={}, left={}, right={})".format(type(self).__name__, self.val, self.left, self.right)
Now I try to add a method that checks if a element exists:
def exists(self, val):
if self.val == val:
return True
return self.left.exists(val) or self.right.exists(val)
Then if I try to search 1, it's fine. But with 2, It errors out.
Traceback (most recent call last):
File "...", line 30, in <module>
print(tree.exists(2))
File "...", line 20, in exists
return self.left.exists(val) or self.right.exists(val)
File "...", line 20, in exists
return self.left.exists(val) or self.right.exists(val)
AttributeError: 'Node' object has no attribute 'exists'
For reference, here's the tree I made:
tree = Tree(val=1,
left=Tree(val=Node(val=2), left=Node(val=3), right=Node(val=4)),
right=Tree(val=Node(val=2), left=Node(val=3), right=Node(val=4)))
How can I recursively search in a tree?

Just jettison the Node class which serves no purpose whatsoever:
class Tree:
def __init__(self, *, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def exists(self, val):
if self.val == val:
return True
if self.left and self.left.exists(val):
return True
if self.right and self.right.exists(val):
return True
return False
tree = Tree(val=1,
left=Tree(val=2, left=Tree(val=3), right=Tree(val=4)),
right=Tree(val=2, left=Tree(val=3), right=Tree(val=4)))

The bottom of you recursion is not only if you found the value (if self.val == val) but also if you reach a leaf of your tree (if self.isLeaf return false)

The error says "Node" doesn't have a method "exists" which is correct. You didn't write Node's version of "exists." You need to work out what data and method belong to each node, and what data and methods should be on the tree as a whole.
But additionally, you can't look at left or right without checking to see if they are None.
self.left.exists()
should be replaced with
self.left and self.left.exists()
And the same for right

Related

Using comparison operator on custom objects

I have the following code:
from typing import Optional
class Tree:
class Tree:
def __init__(self):
self.root: Optional[Node] = None
def __str__(self) -> str:
return f"{self.root.data}"
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
I have this class. I am trying to find maximum data element in my tree. The algorithm I created keeps a temporary element and updates the temporary element if it finds a value greater than this element. At first, I make my temporary element value 0 and then do the following:
if tree.data > temp:
temp = tree.data
but I get the following error:
TypeError: '>' not supported between instances of 'Node' and 'Node'
How can I use the ">" operator in this situation?
Give an implementation of the __gt__ dunder method for the Node object. This tells Python what basis to compare Node objects on:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def __gt__(self, other):
return self.data > other.data

AttributeError: type object 'BSTNode' has no attribute 'left'

I was trying to construct a Binary Search Tree in python.
This is my node class:
class BSTNode:
def __init__(self,val):
self.left = None
self.right = None
self.val = val
This class contains a function called printTree, which is supposed to print the tree inorder. Here is the printTree function:
def printTree(self,val):
if self.left is not None:
self.left.printTree()
print(self.val)
if self.right is not None:
self.right.printTree()
When I execute the function it gives AttributeError: type object 'BSTNode' has no attribute 'left'
Here is my full code:
class BSTNode:
def __init__(self,val):
self.left = None
self.right = None
self.val = val
def insertNode(self,val):
if self.val:
if val < self.val:
if self.left is None:
self.left = BSTNode(val)
else:
self.left.insertNode(val)
else:
if self.right is None:
self.right = BSTNode(val)
else:
self.right.insertNode(val)
else:
self.val = val
def findval(self,fval):
if (fval == self.val):
print(str(self.val)," data found ")
elif(fval < self.val):
if self.left is None:
print(str(self.val)," data not found")
else:
self.left.findval(fval)
else:
if self.right is None:
print(str(self.val)," data not found")
else:
self.right.findval(fval)
def printTree(self,val):
if self.left is not None:
self.left.printTree()
print(self.val)
if self.right is not None:
self.right.printTree()
root = BSTNode(12)
root.insertNode(6)
root.insertNode(5)
root.insertNode(18)
root.insertNode(15)
root.insertNode(21)
BSTNode.printTree(BSTNode)
You are calling printTree() without arguments:
self.left.printTree()
...
self.right.printTree()
Yet, you defined it to accept val, which is just unused by the way:
def printTree(self,val):
Replace it to:
def printTree(self):
The method printTree() is an instance method, not a #classmethod nor #staticmethod. That means it requires an active instance/object of BSTNode to be called which will be passed as the self argument. So this call is incorrect:
BSTNode.printTree(BSTNode)
It must be:
root.printTree(BSTNode)
Then considering my point-1 above, finally it should be:
root.printTree()
Where root is your current active instance of type BSTNode.
After those fixes, it would be successful
5
6
12
15
18
21
Alternative Solution
If you don't want printTree() to be an instance method, make it a #staticmethod instead.
class BSTNode:
...
#staticmethod
def printTree(self): # I named it self to be close to your real implementation. Ideally, rename it to something like "node" or "obj" to avoid confusion since this is not an instance method.
if self.left is not None:
self.printTree(self.left)
print(self.val)
if self.right is not None:
self.printTree(self.right)
...
BSTNode.printTree(root)
This will produce the same output.

Recursion within a method

In my code I am counting the number of nodes in a complete binary tree and I decided to use a recursive approach. However, when I call my method within itself, I get NameError: name 'countNodes' is not defined.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def countNodes(self, root: TreeNode, count = 0) -> int:
if not root.left and not root.right:
return 1
else:
count += countNodes(self, root.left, count) + countNodes(self, root.right, count)
To answer this question.
When accessing a method of the class instance inside the class you have to use self.method. So in your case you have to use self.countNodes instead of countNodes.

yielding data members in binary search tree

I am trying to implement an iterator to my binary search tree. To achieve this, I am attempting to do an in-order traversal through the tree and yield each individual data member. This will allow me to iterate through each item of the tree.
My function:
def __iter__(self):
"""
in-order traversal of a binary search tree
"""
if self.root is not None:
self.check(self.root)
def check(self, cur_node):
if cur_node is not None:
self.check(cur_node.left)
yield cur_node.data #if I were to print this data member, it would be fine
self.check(cur_node.right)
When testing this function with an iteration such as
for i in tree:
I am receiving this error:
TypeError: iter() returned non-iterator of type 'NoneType'
To implement a recursive generator you cannot just "call" yourself, you need to extract elements and yield them.
Python has a special syntax for this:
yield from expr
where expr is iterable, and it can be seen as a shorthand for
for x in expr:
yield x
Using this you can implement in-order traversal of a tree with something like:
class Node:
def __init__(self, data, left, right):
self.data = data
self.left = left
self.right = right
def __iter__(self):
if self.left:
yield from self.left
yield self.data
if self.right:
yield from self.right
The clue is
iter() returned ....
So you need to return an iterator. Your class is an iterator, so return self
def __iter__(self):
"""
in-order traversal of a binary search tree
"""
if self.root is not None:
self.check(self.root)
return self
You should probably implement __next__ as well to actually yield the value.
So the solution might look like
class Tree:
def __init__(...): ...
def __iter__(self):
return self
def __next__(self):
if self.left is not None:
yield from self.left
yield self.data
if self.right is not None:
yield from self.right
You use yield from here to delegate to the child nodes. See https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator
You do in fact need three yield statements, because you need to traverse both the left and right children, as well as producing the value of the current node.
You generally want your iterator as a separate entity from your data structure, so you can have multiple iterators over your data, and so you can iterate over your data multiple times. Below, I show how you can implement a simple DFS algorithm for a basic BST class.
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def __iter__(self):
return BSTIterator(self)
class BSTIterator:
def __init__(self, root):
self.stack = []
curr = root
while curr:
self.stack.append(curr)
curr = curr.left
def __next__(self):
if not self.stack:
raise StopIteration()
node = self.stack.pop()
val = node.val
node = node.right
while node:
self.stack.append(node)
node = node.left
return val
def __iter__(self):
return self
root = Node(5, Node(3, Node(1), Node(4)), Node(10, (Node(6, None, Node(7)))))
list(root)
# [1, 3, 4, 5, 6, 7, 10]

Implement and/or tree in python

I need to implement an And/or tree in python so I can evaluate boolean expressions,
I had an idea of creating a class that contains andNode, orNode and leafNode. The first two are are internal nodes that must have and or or values, the leafNode must have and integer value and represent the final leaves of the tree.I tried this but it doesn't seem to work:
class Node:
def __init__(self,leaf):
self.orNode = None
self.andNode = None
self.leaf = leaf
class and_or_tree (Node):
def __init__(self):
self.root=None
I need to test if an element exists in the tree, the height and iterate through it.
I think an example of such Leaf and and/or nodes could be something like this:
class Leaf:
def __init__(self, v):
self.val = v;
def __call__(self):
return self.val
class AndNode:
def __init__(self, l, r):
self.left = l;
self.right = r;
def __call__(self):
return self.left() and self.right()
class OrNode:
def __init__(self, l, r):
self.left = l;
self.right = r;
def __call__(self):
return self.left() or self.right()
You can build a tree like this:
print AndNode(Leaf(True), Leaf(False))()
print AndNode(OrNode(Leaf(True), Leaf(False)), Leaf(True))()
Which outputs this:
False
True

Categories

Resources