Inserting a node in a complete binary tree with python - python

How to insert a node in a complete binary tree without using queue DS? I tried the following code:
class TreeNode:
def __init__(self, value=None) -> None:
self.left = None
self.value = value
self.right = None
class Tree:
def __init__(self, root=None) -> None:
self.__root = root if not root else TreeNode(root)
self.__len = 1 if root else 0
def append(self, data, root="_"):
if self.__root.value is None:
self.__root = TreeNode(data)
self.__len += 1
return
root = self.__root if root == "_" else root
if not root:
return False
if root.left is None and root.right is None:
root.left = TreeNode(data)
self.__len += 1
return True
elif root.left is not None and root.right is None:
root.right = TreeNode(data)
self.__len += 1
return True
elif root.left is not None and root.right is not None:
if self.append(data, root.left):
return
else:
self.append(data, root.right)
return
the recursive call of that function always add the new node on the left side of the tree, so what should I do to make it recursively checks the right side too?

First of all, the first line in your append code seems to give a special meaning to the value in the root node. When it is None it is not considered a real node, but to represent an empty tree. This is not a good approach. An empty tree is represented by a __root that is None -- nothing else. I would also suggest to remove the optional data argument from the constructor. Either the constructor should allow an arbitrary number of values or none at all. To allow one is odd and strengthens the idea that a tree could have a special None in its root node.
To the core of your question. There is nice attribute to complete binary trees. The paths from the root to a leaf can be represented by a bit pattern, where a 0 means "go left" and a 1 means "go right". And the path to the "slot" where a new node should be injected has a relationship with the size of the tree once that node has been added:
new size
binary representation
path to new node
1
1
[]
2
10
[left]
3
11
[right]
4
100
[left,left]
5
101
[left,right]
In general the path to the new node is defined by the bits in the binary representation of the new tree size, ignoring the leftmost 1.
This leads to the following code:
class Tree:
def __init__(self) -> None:
self.__root = None
self.__len = 0
def append(self, data):
self.__len += 1
if self.__len == 1: # First node
self.__root = TreeNode(data)
return
node = self.__root
# Iterate all the bits except the leftmost 1 and the final bit
for bit in bin(self.__len)[3:-1]:
node = [node.left, node.right][int(bit)] # Choose side
if self.__len & 1: # Use final bit to determine where child goes:
node.right = TreeNode(data)
else:
node.left = TreeNode(data)

Related

How to fix NoneType error in Python Binary Search Tree?

I'm creating a basic binary search tree program where the nodes have to be strings however, I keep getting this error:
'builtins.AttributeError: 'NoneType' object has no attribute 'addNode'
I'm a bit confused because I thought you had to declare the children nodes as None. My code is below (please excuse the messiness and extra print statements):
class BinarySearchTree:
#constructor with insertion value & left/right nodes as None
def __init__(self, data):
self.data = data
self.left = None
self.right = None
#function to insert node
def addNode(root, data):
#when tree doesn't exist
if root == None:
return BinarySearchTree(data)
else:
#when node is already in tree
if root.data == data:
return root
#smaller values go left
elif data < root.data:
root.left.addNode(data)
#bigger values go right
else:
root.right.addNode(data)
return root
#function to find smallest node value (to help with deletion)
def smallestNode(root):
node = root
#loop goes to lowest left leaf
while(node.left is not None):
node = node.left
return node
#function to delete node
def removeNode(root, data):
if root == None:
return root
#when node to be deleted in smaller than root, go left
if data < root.data:
root.left = root.left.removeNode(data)
#when node to be deleted in bigger than root, go right
elif data > root.data:
root.right = root.right.removeNode(data)
##when node to be deleted in the same as root...
else:
#when node has only 1 or 0 children
if root.right == None:
move = root.left
root = None
return move
elif root.left == None:
move = root.right
root = None
return move
#when node has 2 children, copy then delete smallest node
move = root.right.smallestNode()
root.data = move.data
root.right = root.right.removeNode(move.data)
return root
def findNode(root, data):
#if current node is equal to data value then return the root
if root.data == data or root == None:
return root
#if current node is greater than the data value then, search to the left
elif data < root.data:
return root.left.findNode(data)
#if current node is less than the data value then, search to the right
else:
return root.right.findNode(data)
def createBST(keys):
root = BinarySearchTree(keys[0])
for i in range(1,len(keys)):
root.addNode(keys[i])
return root
print('Hi! Welcome to the Binary Search Tree Builder')
print('Here are your options below:')
print('1) Build new tree')
print('2) Add a node')
print('3) Find a node')
print('4) Delete a node')
choice = int(input('What would you like to do?: '))
if choice == 1:
nodes = list(input("Enter the strings you would like to build your tree from (separate by a space): ").split())
print(nodes)
tree = createBST(nodes)
print(tree)
I'm wondering where exactly is this error coming from and how can I fix it? Also, if you see any other problems occuring in my code, please let me know!
You have conflated the notion of "tree node" and "tree". What you have there is a mixture of the two. Remember, the first parameter to ALL member functions should be "self". Replacing "root" by "self" might make your problem more clear.
So, you might create a BinaryTree class, which has a single member called self.root that holds a BinaryTreeNode object, once you have a node. The nodes will hold the left and right values, each of which will either have None or a BinaryTreeNode object. The node class probably doesn't need much code -- just self.left and self.right.
The BinaryTree class will have an addNode member that knows how to traverse the tree to find the right spot, but when you find the spot, you just set self.left = BinaryTreeNode(...). A node does not know about the tree, so a node does not know how to add new nodes. That's a function of the tree itself.
Does that make your path forward more clear?

debugging my binary tree searching sum algo

Question is very simple we have check whether there is root to leaf path with its sum equal to S(sum given to find).
task:
Complete the function hasPathSum() which takes root node and target sum S as input parameter and returns true if path exists otherwise it returns false.
my code is correct according to my debugging but getting an error for few test cases.
my code below
class Node:
def __init__(self,val):
self.data = val
self.left = None
self.right = None
def hasPathSum(root, S):
'''
:param root: root of given tree.
:param sm: root to leaf sum
:return: true or false
'''
if root==None:
return 0
if S==0:
return 1
if S==root.data:
return 1
if S>root.data:
b=S-root.data
else:
return 0
return hasPathSum(root.left,b) or hasPathSum(root.right,b)
I think you are supposed to check if the current node is a leaf node before returning true
Consider a simple tree:
2
/ \
1 3
Calling hasPathSum(root, 2) would return 1 since S==root.data is true although it is not a path to the leaf node.
You can fix it like:
class Node:
def __init__(self,val):
self.data = val
self.left = None
self.right = None
def hasPathSum(root, S):
'''
:param root: root of given tree.
:param sm: root to leaf sum
:return: true or false
'''
if root==None:
return 0
# we dont need the S == 0 condition
# since the sum can reach 0 before reaching the leaf node
# check if root is leaf
if S==root.data and root.left == None and root.right == None:
return 1
if S>root.data:
b=S-root.data
else:
return 0
return hasPathSum(root.left,b) or hasPathSum(root.right,b)

Binary Tree Preorder Traversal

I'm just getting started with binary trees and I have this task where I have to do a preorder iterative traversal search for a given binary tree '[1,null,2,3]'.
I tried to use a new binarytree module that I found, but it didn't worked and I saw a youtube video where some guy did it recursively but I just can't figure out.
#Input = [1,null, 2,3]
# 1
# \
# 2
# /
# 3
#Expected output = [1,2,3]
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
I'm just clueless, I wrote an algorithm but I can't turn it into actual functional code. Also I don't understand how the root: TreeNode works. Does it turn every element of the list into a TreeNode object? So far my best try had been this and it's obviously wrong in many ways.
def preorderTraversal(self, root: TreeNode) -> List[int]:
result = []
for i in root:
if i =! root[0] and root.left =! None:
root.left = i
if root.left =! null:
root.left.left = i
elif root.left == null:
root.right.left = i
elif root.left
result.append(i)
elif root.right == None:
root.right = i
else:
continue
A few points:
Function preorderTraversal should have no self parameter; it is not a method of a class.
I have modified the TreeNode class to make it more convenient to specify its children TreeNode objects.
Function preorderTraversal takes as an argument a TreeNode object. As I mentioned in a comment, your statement, #Input = [1,null, 2,3], is difficult to make sense of.
You need to keep a last in/first out (LIFO) stack of unvisited TreeNode objects to implement an iterative (rather than recursive solution). In the code below, variable nodes serves that purpose.
The code:
from typing import List
class TreeNode:
def __init__(self, val, left=None, right=None):
self.x = val
self.left = left
self.right = right
n3 = TreeNode(3)
n2 = TreeNode(2, left=n3)
n1 = TreeNode(1, right=n2)
def preorderTraversal(root: TreeNode) -> List[int]:
result = []
nodes = []
nodes.append(root) # initial node to visit
while len(nodes): # any nodes left top visit?
node = nodes.pop() # get topmost element, which is the next node to visit
result.append(node.x) # "output" its value before children are visited
if node.right is not None:
# show this node must be visited
nodes.append(node.right) # push first so it is popped after node.left
if node.left is not None:
# show this node must be visited
nodes.append(node.left)
return result
print(preorderTraversal(n1))
Prints:
[1, 2, 3]
Or a more complicated tree:
10
/ \
8 2
/ \ /
3 5 2
n3 = TreeNode(3)
n5 = TreeNode(5)
n8 = TreeNode(8, left=n3, right=n5)
n2a = TreeNode(2)
n2b = TreeNode(2, left=n2a)
n10 = TreeNode(10, left=n8, right=n2b)
print(preorderTraversal(n10))
Prints:
[10, 8, 3, 5, 2, 2]
You can use the queue data structure for it.
queue = []
result = []
queue.append(root)
while queue:
node = queue.pop()
result.append(node.val)
if node.left is not None:
queue.insert(0, node.left)
if node.right is not None:
queue.insert(0, node.right)
return result
This is a problem from leetcode platform, here is my solution. Runtime: 28 ms, faster than 81.08% of Python3 online submissions for Binary Tree Preorder Traversal.
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)

Find number of elements smaller than a given element in BST

I have been struggling with this problem for a while and I am a Python beginner when it comes to BST, so I would appreciate some help. I am dynamically adding elements from an (unsorted) array into BST. That part is fine, I know how to do that. The next step, proved to be impossible with my current skill set. As I am adding elements to the tree, I need to be able to find current rank of any element in the tree. I know there are subtleties in this problem, so I would need help to at least find the number of nodes that are below the given node in BST. For example, in this case, node 15 has nodes 10,5 and 13 below it, so the function will return 3. Here is my existing code [this is a problem from Cracking the coding interview, chapter 11]
class Node:
"""docstring for Node"""
def __init__(self, data):
self.data = data
self.left=None
self.right=None
self.numLeftChildren=0
self.numRightChildren=0
class BSTree:
def __init__(self):
self.root = None
def addNode(self, data):
return Node(data)
def insert(self, root, data):
if root == None:
return self.addNode(data)
else:
if data <= root.data:
root.numLeftChildren+=1
root.left = self.insert(root.left, data)
else:
root.numRightChildren+=1
root.right = self.insert(root.right, data)
return root
def getRankOfNumber(self,root,x):
if root==None:
return 0
else:
if x>root.data :
return self.getRankOfNumber(root.right,x)+root.numLeftChildren+1
elif root.data==x:
return root.numLeftChildren
else:
return self.getRankOfNumber(root.left,x)
BTree=BSTree()
root=BTree.addNode(20)
BTree.insert(root,25)
BTree.insert(root,15)
BTree.insert(root,10)
BTree.insert(root,5)
BTree.insert(root,13)
BTree.insert(root,23)
You can go by this approach:
1. Have 2 more fields in each node numLeftChildren and numRightChildren.
2. Initialize both of them to 0 when you create a new node.
3. At the time of insertion, you make a comparison if the newly added node's
key is less than root's key than you increment, root's numLeftChildren and
call recursion on root's left child with the new node.
4. Do Same thing if new node's key is greater than root's key.
Now, come back to your original problem, You have to find out the number of children in left subtree. Just find out that node in O(logN) time and just print the numLeftChildren
Time Complexity: O(logN)
PS: I have added another field numRightChildren which you can remove if you are always interested in knowing the number of nodes in left subtree only.
You could modify your BST to contain the number of nodes beneath each node.
Or you could iterate over a traditional BST from least to greatest, counting as you go, and stop counting when you find a node of the required value.
You could simplify the code by using just instances of the Node class, as your BSTree instance operates on the root node anyway. Another optimization could be to not represent duplicate values as separate nodes, but instead use a counter of occurrences of the key:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
self.num_left_children = 0
self.occurrences = 1
def insert(self, data):
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
self.num_left_children += 1
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.occurrences += 1
def get_rank(self, data):
if data < self.data:
return self.left.get_rank(data)
elif data > self.data:
return (self.occurrences + self.num_left_children +
self.right.get_rank(data))
else:
return self.num_left_children
Demo:
root = Node(20)
root.insert(25)
root.insert(15)
root.insert(10)
root.insert(10)
root.insert(5)
root.insert(13)
root.insert(23)
print(root.get_rank(15)) # 4
You can use the below function to find the number of the nodes in the tree (including the root).
def countNodes(self, root):
if root == None:
return 0
else
return (1 + countNodes(root.left) + countNodes(root.right));
To find the number of nodes that lie below root, subtract 1 from the value returned by the function. I think this will help get you started on the problem.
Your code will look like:
class Node:
"""docstring for Node"""
def init(self, data):
self.data = data
self.left=None
self.right=None
self.depth=0
class BSTree:
def init(self):
self.root = None
def addNode(self, data):
return Node(data)
def insert(self, root, data):
if root == None:
return self.addNode(data)
else:
if data <= root.data:
root.left = self.insert(root.left, data)
else:
root.right = self.insert(root.right, data)
return root
BTree=BSTree()
root=BTree.addNode(20)
BTree.insert(root,25)
BTree.insert(root,15)
BTree.insert(root,10)
BTree.insert(root,5)
BTree.insert(root,13)
BTree.insert(root,23)
BTree.insert(root,23)
numLeft = countNodes(root->left);
numRight = countNodes(root->right);
numChildren = numLeft + numRight;

Print BST in pre order

rI am trying to print out my binary tree in pre order form however I am coming across these errors. I am still learning python so I am not quite sure what is going on. But I assume that my print function isn't working properly. Not quite sure why preorder_print is having a global name issue though =/
my expected output would be
pre order:
4
2
1
3
8
6
10
Output:
pre order:
<BST_tree.Node instance at 0x0000000002AA0988>
<BST_tree.Node instance at 0x0000000002AA0E08>
<BST_tree.Node instance at 0x0000000002AA0E88>
my code:
class Node:
def __init__(self,value):
self.right = None
self.left = None
self.value = value
def BST_Insert(root, node): # root --> root of tree or subtree!
if root.value is None:
root = node # beginning of tree
else:
if root.value > node.value: # go to left
if root.left is None:
root.left = node
else:
BST_Insert(root.left, node)
else:
if root.value < node.value: # go to right
root.right = node
else:
BST_Insert(root.right, node)
def preorder_print(root):
print root
if root.left is not None:
preorder_print(root.left)
else:
if root.right is not None:
preorder_print(root.right)
r = Node(4)
# left
a = Node(2)
b = Node(1)
c = Node(3)
# right
d = Node(8)
e = Node(6)
f = Node(10)
BST_Insert(r, a)
BST_Insert(r, b)
BST_Insert(r, c)
BST_Insert(r, d)
BST_Insert(r, e)
BST_Insert(r, f)
print "pre order:"
preorder_print(r)
* EDIT *
Thank you everyone and especially abarnert for your help!!! Here is the fixed version! or the preorder_print and BST_Inert
def BST_Insert(root, node): # root --> root of tree or subtree!
if root.value is None:
root = node # beginning of tree
else:
if root.value > node.value: # go to left
if root.left is None:
root.left = node
else:
BST_Insert(root.left, node)
if root.value < node.value: # go to right
if root.right is None:
root.right = node
else:
BST_Insert(root.right, node)
def preorder_print(root):
print root.value
if root.left is not None:
preorder_print(root.left)
if root.right is not None:
preorder_print(root.right)
Apologies for posting two answers, but I'm not sure which of two problems you're asking about here.
Your preorder traversal isn't covering the entire tree, because you ignore the entire right subtree whenever the left subtree isn't empty:
def preorder_print(root):
print root
if root.left is not None:
preoder_print(root.left)
else:
if root.right is not None:
preorder_print(root.right)
So, in your example, because the 4 node has the 2 node on its left, it won't look at 8 or anything underneath it. And then the same thing in the 2. So, you only get 3 nodes instead of all 7.
To fix this, just remove the else:
def preorder_print(root):
print root
if root.left is not None:
preoder_print(root.left)
if root.right is not None:
preorder_print(root.right)
You also have a problem in your BST_Insert function. You're setting root.right = node any time node.value > root.value, even if there's already something there. So, the first time you try to insert something that's on the left side of the right side of anything, it will erase the parent—the 6 erases the 8, then the 10 erases the 6, so you end up with just 4, 2, 1, 3, and 10.
I think what you wanted here is to change this:
else:
if root.value < node.value: # go to right
root.right = node
else:
BST_Insert(root.right, node)
… to:
elif root.value < node.value: # go to right
if root.right is None
root.right = node
else:
BST_Insert(root.right, node)
The print function is working just fine. When you print out an object that doesn't have a custom __repr__ or __str__ method, this is exactly what you're supposed to get.
There are two ways to solve this.
First, instead of printing the Node object itself, print the information you want to print. For example, change this:
print root
… to:
print 'node with value {}'.format(root.value)
… or:
print root.value
… or:
print 'I've got a node and he's got a value and it's ' + str(root.value)
Alternatively, if you always want nodes to print out the same way—e.g., Node(4)—you can give the class a __repr__ method:
def __repr__(self):
return 'Node({})'.format(self.value)
Sometimes you want to provide both a nice human-readable representation of a class that you might put into a report, and a different representation that's useful for, e.g., experimenting at the interactive interpreter. In that case, you define both __str__ and __repr__:
def __str__(self):
# Pick whatever you think looks nice here
return str(self.value)
# return 'Node: ' + str(self.value)
# return 'Node with value {}'.format(self.value)
def __repr__(self):
return 'Node({})'.format(self.value)
(Notice that Node(4) is a nice "experimenting at the interactive interpreter" representation, because it's exactly what you'd type into the interpreter to create an equivalent object.)
Use print root.value instead of print root.
Explanation:
root is an object, an instance of the Node class. root.value is the actual number the node holds.
Aside: the "proper" way to do this would be what #abarnert answered, via __repr__, but it's a little overkill for simple exercises focused around teaching about trees.
You want to print the value of root
print root.value

Categories

Resources