I'm working on a project where we are supposed to implement a BST via dataclass. The first "part" of the code works. It sorts 6 entries (key/value combo) according to size. However, the next step is to replace some values in the BST. I'm almost certain that the best way to do this is by checking the entire tree for key matches and then replacing the value of said key with the new value. However, the code only "semi" works.
#dataclass
class Node:
key: Any = None # the key
value: Any = None # the value
left: Any = None # left child (a Node)
right: Any = None # right child (a Node)
def put(self, key, value):
new_leaf = Node(key, value) # Below SHOULD check if there is a key match in either the
#left or right leaf.
if self.left or self.right is not None:
if self.left is not None: #Checks left leaf for key match.
if self.left.key == new_leaf.key:
print("Match on SELF.LEFT.KEY:", self.left.key, new_leaf.key)
if self.right is not None: #Checks right leaf for key match.
if self.right.key == new_leaf.key:
print("Match on SELF.RIGHT.KEY:", self.right.key, new_leaf.key)
if new_leaf.value < self.value: #Adds new leafs normally. This part (should) work(?).
if self.left is None:
self.left = Node(key, value, None, None)
else:
self.left.put(key, value)
if new_leaf.value > self.value:
if self.right is None:
self.right = Node(key, value, None, None)
else:
self.right.put(key, value)
Here is the original output (when just adding key/values):
{ (Adam|27) (Ceve|37) (Ella|39) (Owen|40) (Zoe|41) (Fred|44)}
And here is the output when I try to replace key Ceve and key Zoe by adding them to the put method (Match on SELF.LEFT.KEY means that an identical key has been found in the tree and is to be replaced (later)):
Override existing values
Match on SELF.LEFT.KEY: Zoe Zoe
{ (Adam|27) (Ceve|37) (Ella|39) (Owen|40) (Zoe|41) (Fred|44) (Zoe|99) (Ceve|100)}
Now, it should print two "matches" but for some reason it doesn't detect Ceve. I've messed around with print functions to try and actually see whats going on and it seems to be that it never actually finds the Ceve leaf (obviously), but I don't know why that is.
Does anyone have any pointers as to what could be causing the issue?
Related
I'm starting to study binary trees, and found this code in a teaching website
class Node:
def __init__(self, data):
self.left = None
self.right = None
self.data = data
def insert(self, data):
# Compare the new value with the parent node
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
# Print the tree
def PrintTree(self):
if self.left:
self.left.PrintTree()
print( self.data),
if self.right:
self.right.PrintTree()
# Use the insert method to add nodes
root = Node(27)
root.insert(14)
root.insert(35)
root.insert(31)
root.insert(10)
root.insert(19)
root.PrintTree()
And after debugging said code, i noticed that the correct root value was set at the beggining, but after a few steps changed to another value.
At the end, the root value was set with the correct int (the first value), but i can't understand why.
Also, is there a better way to create a binary tree and insert values in python?
I think you're being fooled by the debugger. After the first three statements, you'll have three objects: One named root, one named root.left, one named root.right:
root.data = 27
root.left.data = 14
root.right.data = 35
If you were then tracing through inserting 31, you'd get to the part in insert where it calls self.right.insert(data). At that point, you'll start insert again, but now self is the node in root.right. It's not root any more.
Good day, I'm working on a home work that requires me to delete a target value, value, from a binary search tree, tnode; it also requires me to return a tuple. I adapted the code I found for Python 2. But its giving me an Attribute Error whenever I try to run it. I've been trying to figure out how to fix these to no avail. Can anybody point out what I'm doing wrong? Here's what I have.
def delete_prim(tnode, value):
"""
Delete a value from the binary search tree.
Preconditions:
:param tnode: a TreeNode with the BST property
:param value: a value
Postconditions:
If the value is in the tree, it is deleted.
If the value is not there, there is no change to the tree.
Return
:return: tuple (flag, tree)
flag is True if deletion succeeded;
tree is the resulting without the value
flag is False if the value was not in the tree,
tree returned unchanged
"""
def minimum_value(tnode):
"""This function searches for the minimum value in the tree."""
cur = tnode
while cur.left is not None:
cur = cur.left
return cur
if tnode is None:
return False, tnode
if value < tnode.data:
tnode.left = delete_prim(tnode.left,value)
elif value > tnode.data:
tnode.right = delete_prim(tnode.right,value)
elif tnode.left is None:
return tnode.right
elif tnode.right is None:
return tnode.left
else:
tnode.data = minimum_value(tnode.right)
tnode.right = delete_prim(tnode.right, tnode.data)
return False, tnode
Edit: I got the code working for some part, but I found a problem that it wont delete a node that has children.
Edit: Tree node implementation.
class TreeNode(object):
def __init__(self, data, left=None, right=None):
"""
Create a new TreeNode for the given data.
Pre-conditions:
data: Any data value to be stored in the KVTreeNode
left: Another KVTreeNode (or None, by default)
right: Another KVTreeNode (or None, by default)
"""
self.data = data
self.left = left
self.right = right
I am trying to understand binary trees, but doing so has brought me to confusion about how class instances interact, how does each instance link to another?
My Implementation:
class Node(object):
def __init__(self, key):
self.key= key
self.L = None
self.R = None
class BinaryTree(object):
def __init__(self):
self.root = None
def get_root(self):
return self.root
def insert(self, key):
if self.get_root()==None:
self.root = Node(key)
else:
self._insert(key, self.root)
def _insert(self, key, node):
if key < node.key:
if node.L == None:
node.L = key
else:
self._insert(key, Node(node.L))
if key > node.key:
if node.R == None:
node.R = key
else:
self._insert(key, Node(node.R))
myTree= BinaryTree()
A Scenario
So lets say I want to insert 10, I do myTree.insert(10) and this will instantiate a new instance of Node(), this is clear to me.
Now I want to add 11, I would expect this to become the right node of the root node; i.e it will be stored in the attribute R of the root node Node().
Now here comes the part I don't understand. When I add 12, it should become the child of the root nodes right child. In my code this creates a new instance of Node() where 11 should the be key and 12 should be R.
So my question is 2-fold: what happens to the last instance of Node()? Is it deleted if not how do I access it?
Or is the structure of a binary tree to abstract to think of each Node() connected together like in a graph
NB: this implementation is heavily derived from djra's implementation from this question How to Implement a Binary Tree?
Make L and R Nodes instead of ints. You can do this by changing the parts of your _insert function from this:
if node.L == None:
node.L = key
to this:
if node.L == None:
node.L = Node(key)
There is also a problem with this line:
self._insert(key, Node(node.L))
The way you're doing it right now, there is no way to access that last reference of Node() because your _insert function inserted it under an anonymously constructed node that has no parent node, and therefore is not a part of your tree. That node being passed in to your insert function is not the L or R of any other node in the tree, so you're not actually adding anything to the tree with this.
Now that we changed the Ls and Rs to be Nodes, you have a way to pass in a node that's part of the tree into the insert function:
self._insert(key, node.L)
Now you're passing the node's left child into the recursive insert, which by the looks of thing is what you were originally trying to do.
Once you make these changes in your code for both the L and R insert cases you can get to the last instance of Node() in your
10
\
11
\
12
example tree via myTree.root.R.R. You can get its key via myTree.root.R.R.key, which equals 12.
Most of you're questions come from not finishing the program; In your current code after myTree.insert(11) you're tree is setting R equal to a int rather than another Node.
If the value isn't found then create the new node at that point. Otherwise pass the next node into the recursive function to keep moving further down the tree.
def _insert(self, key, node):
if key < node.key:
if node.L == None:
node.L = Node(key)
else:
self._insert(key, node.L)
if key > node.key:
if node.R == None:
node.R = Node(key)
else:
self._insert(key, node.R)
P.S. This isn't finished you're going to need another level of logic testing incase something is bigger than the current Node.key but smaller than the next Node.
I wish to find the parent to a node with a certain value in a BST. My node class has attributes item (i.e the value/key), left and right.
The idea to find a parent is like this:
1) If the value (key) does not exist, return None, None
2) If the root is equal to the value (key) then return None, root
3) Else find the value (key) and return (par, node) where par is the parent and node
My function looks like this:
def findpar(self,key):
if not self._exists(key):
return None,None
elif self.root.item==key:
return None, self.root
p=self.root
found=False
while not found:
if p.left.item==key:
return p, p.left
found=True
elif p.right.item==key:
return p, p.right
found=True
elif p.item<key:
p=p.left
elif p.item>key:
p=p.right
How do I handle the issue when p.left or p.right is None?
As your code currently works, it is impossible that you're turning toward a None left or right child. This is because your code starts with
if not self._exists(key):
return None,None
So key must exist, and if it must exist, it must exist on the search path.
It should be noted that you're effectively performing the search twice, though, which is not that efficient. Instead, you could try something like this:
def findpar(self,key):
parent, node = None, self.root
while True:
if node is None:
return (None, None)
if node.item == key:
return (parent, node)
parent, node = node, node.left if key < node.item else node, node.right
For example, a tree like this:
5
/ \
3 6
/ \
7 2
print(tree.branchLenSum())
will be 1+1+2+2=6
Tree class:
class BinaryTree:
# Constructor, takes in new key value
def __init__(self, myKey):
self.key = myKey
self.leftChild = None
self.rightChild = None
# Returns root key value
def getRootValue(self):
return self.key
# Changes root key value
def setRootValue(self, newKey):
self.key = newKey
# Returns reference to left child
def getLeftChild(self):
value=None
if self.leftChild!=None:
value=self.leftChild
return value
# Returns reference to right child
def getRightChild(self):
value=None
if self.rightChild!=None:
value = self.rightChild
return value
def insertLeftChild(self, childKey):
newNode = BinaryTree(childKey)
newNode.leftChild = self.leftChild
self.leftChild = newNode
# Inserts key as right child. Existing right child becomes new right child
# of new key
def insertRightChild(self, childKey):
newNode = BinaryTree(childKey)
newNode.rightChild = self.rightChild
self.rightChild = newNode
The tree I have built for the example:
tree=BinaryTree(5)
tree.insertLeftChild(3)
tree.insertRightChild(6)
nodeA=tree.getLeftChild()
nodeA.insertLeftChild(7)
nodeA.insertRightChild(2)
What I have so far:
def branchLenSum(self):
rounds=0
if self.getLeftChild() ==None and self.getRightChild()==None:
return rounds+rounds+1
else:
rounds+=rounds+1
if self.getLeftChild()!=None:
rounds+=self.getLeftChild().branchLenSum()
if self.getRightChild()!=None:
rounds+=self.getRightChild().branchLenSum()
return rounds
My idea is that every time travel to next node, counter adds 1+counter itself. I think this will get all the length sum.
Okay, so the reason why you only get a result of 5 is rather simple: What you are doing is count the nodes. So in your case, you have 5 nodes, so the result is 5.
If you want to get the internal path length, then I believe you will have to keep track of the current depth while navigating through the tree. You can do this simply by using an optional parameter.
def branchLenSum(self, depth = 0):
rounds = depth
if self.leftChild:
rounds += self.leftChild.branchLenSum(depth + 1)
if self.rightChild:
rounds += self.rightChild.branchLenSum(depth + 1)
return rounds
In this case, whenever we navigate down to a child, we increase the current depth by one. And when counting the branch length of a node, we start at the depth.
Btw. note that officially, the internal path length is defined as the length for only the internal nodes, i.e. not leaves. The method above counts every node including leaves. If you want to follow the official definiton, you will have to add a leaf-check at the beginning and return 0 for leaves.
Some other things:
The methods getLeftChild and getRightChild do effectively nothing. You assign None to the return value, then check if the left/right child is None and if that’s not the case you assign the child to the return value and return it.
So essentially, you are returning self.leftChild/self.rightChild; there’s no need to actually look at the value and check for None.
In Python, you usually don’t use accessor or mutator methods (getters/setters); you just access the underlying property itself. This makes the methods getLeftChild, getRightChild, getKey and setKey redundant.
Checking for None with != None or == None is an antipattern. If you want to check if, for example a child is not None, just do if child. And if you want to check if it is not set (i.e. not None) just do if not child.