Python Binary Search Tree with key and value - python

I need to implement a Binary Search Tree class as homework but I struggle making the insert function. I have looked through Google a lot to find some solutions or possibilities on how to do it but none of them has used a key and value (mostly just value) or if they used a key aswell, they had tons of seperate functions which I am not allowed to do I think.
So the pre-built is simply that:
class Node:
def __init__(self, key, value):
self.key = key
self.value = value
self.left = self.right = None
class BinarySearchTree:
def __init__(self):
self.root = None
self.size = 0
def __len__(self):
return self.size
def insert(self, key, value):
pass
def remove(self, key):
pass
def find(self, key):
pass
Now the thing is, if I want to check for example whether the value is smaller or bigger than a current Node to put it either right or left, I get Errors such as "root is not defined" or "root.right" has no such attribute etc...
And I guess that makes sense because self.root is declared as None.
But how do I actually fix it now to make the insert function work?
I am a little confused by this task as it uses key + value, so I need to insert the value bound to the specific key and in case the key already existed, overwrite its value.

its 5 in the morning so this might be all wrong, but here goes:
the key is what we are sorting by, the values aren't interesting
your insert function should probably look something like this:
def insert(self, key, value):
if self.root = None:
self.root = Node(key,value)
return
#regular binary tree traversal (comparing the key) to find where to insert, lets assume we need to insert on the left
parent.left = Node(key,value)
can you figure it out from here or would you like more direction

You didn't specify, but I'm guessing the point of the keys is to determine if a particular key is already in the tree, and if so, replace the related node's value in O(1) runtime complexity.
So when you're inserting a node, you will first check the dictionary for the key (you will initialize an empty dictionary yourself in __init__). If it already is there, then you simply just need to replace the value of the node for that particular key. Otherwise, you add the new node the same way that you would in any BST, and also remember to update your dictionary to map the key to it's node.

Related

How to move last element of a linked list to first in python (below code)?

#DSA-Prac-1
class Node:
def __init__(self,data):
self.__data=data
self.__next=None
def get_data(self):
return self.__data
def set_data(self,data):
self.__data=data
def get_next(self):
return self.__next
def set_next(self,next_node):
self.__next=next_node
class LinkedList:
def __init__(self):
self.__head=None
self.__tail=None
def get_head(self):
return self.__head
def get_tail(self):
return self.__tail
def add(self,data):
new_node=Node(data)
if(self.__head is None):
self.__head=self.__tail=new_node
else:
self.__tail.set_next(new_node)
self.__tail=new_node
def insert(self,data,data_before):
new_node=Node(data)
if(data_before==None):
new_node.set_next(self.__head)
self.__head=new_node
if(new_node.get_next()==None):
self.__tail=new_node
else:
node_before=self.find_node(data_before)
if(node_before is not None):
new_node.set_next(node_before.get_next())
node_before.set_next(new_node)
if(new_node.get_next() is None):
self.__tail=new_node
else:
print(data_before,"is not present in the Linked list")
def display(self):
temp=self.__head
while(temp is not None):
print(temp.get_data())
temp=temp.get_next()
def find_node(self,data):
temp=self.__head
while(temp is not None):
if(temp.get_data()==data):
return temp
temp=temp.get_next()
return None
def delete(self,data):
node=self.find_node(data)
if(node is not None):
if(node==self.__head):
if(self.__head==self.__tail):
self.__tail=None
self.__head=node.get_next()
else:
temp=self.__head
while(temp is not None):
if(temp.get_next()==node):
temp.set_next(node.get_next())
if(node==self.__tail):
self.__tail=temp
node.set_next(None)
break
temp=temp.get_next()
else:
print(data,"is not present in Linked list")
def change_order(input_list):
'I need the code to be written here'
return input_list
input_list=LinkedList()
input_list.add(9)
input_list.add(3)
input_list.add(56)
input_list.add(6)
input_list.add(2)
input_list.add(7)
input_list.add(4)
result=change_order(input_list)
result.display()
Only the function change_order must be written. No changes should be made in other parts of the program. The input linked list is 9->3->56->6->2->7->4 and the output should be 4->9->3->56->6->2->7.I need answer for this particular code.
This is what i have tried. Since the head of linkedlist class is a private attribute, i face difficulty in assigning the new head.
def change_order(input_list):
temp=input_list.get_head()
while temp and temp.get_next():
sec_last = temp
temp=temp.get_next()
sec_last.set_next(None)
temp.set_next(input_list.get_head())
Your LinkedList class should already provide all the functionality you need to be able to accomplish this without any need to mess around with the pointers yourself:
data = input_list.get_tail().get_data() # get last element
input_list.delete(data) # remove last element
input_list.insert(data, None) # insert that element first
Note that the list interface assumes that all items in the list are unique; some of these methods don't work properly if you have duplicates and will only ever operate on the first match. If you do the insert before the delete, for example, the delete will remove the item at the head that you just inserted, not the item at the tail that you wanted to remove.
This is essentially a bug/constraint of this list implementation; normally a list interface would provide you with some sort of iterator to allow you to handle cases with multiple matches.
If you had to work around that under the parameters of the assignment, being able to modify the head isn't the hard part (because you can do that reliably via insert), but rather popping off the tail (since the only interface that lets you do that is delete, which will flatly not let you access a node by reference, and is therefore always going to do the wrong thing if you're trying to use it to delete the tail when there's a duplicate). The simplest solution IMO is to just convert the entire list into a better format, do whatever you need, and then convert it back. (This is a terrible lesson if the goal is to learn how linked lists work, but it's a good lesson if the goal is learn how sometimes you need to do silly things to work around someone else's incompetence.)
def change_order(input_list):
'I need the code to be written here'
# Define helper functions to convert LinkedList to and from List.
def convert_linked_list_to_list(linked_list):
"""Converts a LinkedList to a native List."""
arr = []
node = input_list.get_head()
while node is not None:
arr.append(node.get_data())
node = node.get_next()
return arr
def rebuild_linked_list_from_list(linked_list, arr):
"""Replaces input LinkedList contents with native List contents."""
while linked_list.get_head() is not None:
linked_list.delete(linked_list.get_head().get_data())
for data in arr:
linked_list.add(data)
# Now do the order change using a List.
arr = convert_linked_list_to_list(input_list)
rebuild_linked_list_from_list(input_list, arr[-1:] + arr[:-1])
return input_list
In real life, you'd define these helpers outside of this function, because they're bound to be useful in other situations where you need to manipulate one of these LinkedList containers in ways that its terrible interface doesn't support, but the parameters of the assignment require everything to be contained to change_order, so there you have it.

Deleting node in BST (python)

Trying to build a binary search tree in python and came across this weird bug. After deleting nodes using my delete_node function, deleted nodes are still being printed, but only ones that are being deleted properly are nodes that have two other nodes attached to it (these ones are supposed to be hardest to delete though)
Here's the code:
class Node:
def __init__(self, data):
self.Left = self.Right = None
self.T_data = data
# function that deletes nodes from the tree
def delete_node(self, item):
if self is None:
return self
elif item < self.T_data:
self.Left.delete_node(item)
elif item > self.T_data:
self.Right.delete_node(item)
else:
# case when the node we want to delete has no leaves attached to it
if self.Right is None and self.Left is None:
self = None
# cases when a node has either left or right leaf node
elif self.Left is None:
temp = self.Right
self = None
return temp
elif self.Right is None:
temp = self.Left
self = None
return temp
else: #case when a node has two leaf nodes attached
temp = self.Right.min_node()
self.T_data = temp.T_data
self.Right = self.Right.delete_node(temp.T_data)
return self
As you can see the way nodes are deleted is using a recursion, so for double-branched nodes to get deleted, the single-branch node deletion should work properly, but it does not.
heres the print function and how the functions are called:
# function that prints contents of the tree in preorder fashion
def print_tree_preorder(self):
if self is None:
return
print("%s" % self.T_data)
if self.Left is not None:
self.Left.print_tree_preorder()
if self.Right is not None:
self.Right.print_tree_preorder()
x = int(input("Which element would you like to delete?\n"))
root = root.delete_node(x)
root.print_tree_preorder()
What you're doing right now, when you have:
self = None
Is not actually deleting the object itself. What you're doing is assigning self to a different value.
I think a good way to illustrate this problem is thinking of self and other variables as a tag.
When you say:
a = 3
You are essentially having the tag a put on the entity 3. 3 resides somewhere in memory, and a "points" to 3(although pointers in C++ isn't really the references in python, so be careful if you're going to make that comparison).
When you point self to None, what you wanted to say was:
So I want to remove this object, and all things that point to this object will point to None instead.
However, what you're currently saying is:
So I want to set my self tag to point to None.
Which is completely different. Just because you set your self tag to None does not mean you set the node's parents .Right or .Left members to None as well.
The solution? Well, you're not gonna like this, but you're gonna have to either:
have a pointer to the parent for each node, and set the parent's child(this child specifically) to None.
check 1 levels deeper in your tree, so you can delete the child node instead of deleting the node itself.
The reason the case for 2 node children works is because you're setting the attribute of the object here, instead of setting self=None. What this means is that you're still pointing to the same object here, specifically on this line:
self.T_data = temp.T_data
It's the difference between "Coloring a object does not make it a different object. Its traits are just different" vs. "replacing a object with another object makes it a different object".

Use __get__, __set__ with dictionary item?

Is there a way to make a dictionary of functions that use set and get statements and then use them as set and get functions?
class thing(object):
def __init__(self, thingy)
self.thingy = thingy
def __get__(self,instance,owner):
return thingy
def __set__(self,instance,value):
thingy += value
theDict = {"bob":thing(5), "suzy":thing(2)}
theDict["bob"] = 10
wanted result is that 10 goes into the set function and adds to the existing 5
print theDict["bob"]
>>> 15
actual result is that the dictionary replaces the entry with the numeric value
print theDict["bob"]
>>> 10
Why can't I just make a function like..
theDict["bob"].add(10)
is because it's building off an existing and already really well working function that uses the set and get. The case I'm working with is an edge case and wouldn't make sense to reprogram everything to make work for this one case.
I need some means to store instances of this set/get thingy that is accessible but doesn't create some layer of depth that might break existing references.
Please don't ask for actual code. It'd take pages of code to encapsulate the problem.
You could do it if you can (also) use a specialized version of the dictionary which is aware of your Thing class and handles it separately:
class Thing(object):
def __init__(self, thingy):
self._thingy = thingy
def _get_thingy(self):
return self._thingy
def _set_thingy(self, value):
self._thingy += value
thingy = property(_get_thingy, _set_thingy, None, "I'm a 'thingy' property.")
class ThingDict(dict):
def __getitem__(self, key):
if key in self and isinstance(dict.__getitem__(self, key), Thing):
return dict.__getitem__(self, key).thingy
else:
return dict.__getitem__(self, key)
def __setitem__(self, key, value):
if key in self and isinstance(dict.__getitem__(self, key), Thing):
dict.__getitem__(self, key).thingy = value
else:
dict.__setitem__(self, key, value)
theDict = ThingDict({"bob": Thing(5), "suzy": Thing(2), "don": 42})
print(theDict["bob"]) # --> 5
theDict["bob"] = 10
print(theDict["bob"]) # --> 15
# non-Thing value
print(theDict["don"]) # --> 42
theDict["don"] = 10
print(theDict["don"]) # --> 10
No, because to execute theDict["bob"] = 10, the Python runtime doesn't call any methods at all of the previous value of theDict["bob"]. It's not like when myObject.mydescriptor = 10 calls the descriptor setter.
Well, maybe it calls __del__ on the previous value if the refcount hits zero, but let's not go there!
If you want to do something like this then you need to change the way the dictionary works, not the contents. For example you could subclass dict (with the usual warnings that you're Evil, Bad and Wrong to write a non-Liskov-substituting derived class). Or you could from scratch implement an instance of collections.MutableMapping. But I don't think there's any way to hijack the normal operation of dict using a special value stored in it.
theDict["bob"] = 10 is just assign 10 to the key bob for theDict.
I think you should know about the magic methods __get__ and __set__ first. Go to: https://docs.python.org/2.7/howto/descriptor.html Using a class might be easier than dict.

InOrder Traversal in Python

The problem I am tackle with is to find the first occurrence node in its inorder traversal in a BST.
The code I have is given below
def Inorder_search_recursive(node,key):
if not node:
return None
InOrder_search_recursive(node.lChild)
if node.value==key:
return node
InOrder_search_recursive(node.rChild)
This code always return None, what's wrong with it. I think I've return node when I find a node with value k. Why cannot python pass this node???Thanks in advance
When you call yourself recursively, like this:
InOrder_search_recursive(node.lChild)
That's just a normal function call, like any other. It just calls the function and gets back a result. It doesn't automatically return the value from that function, or do anything else.
So, you do the left-subtree search, ignore the results, then go on to check node.value == key, and, if that fails, you do the right-subtree search, again ignore the results, and fall off the end of the function, meaning you return None.
To make this work, you need to return the value you got back. But, of course, only if it's not None.
Also, you forgot to pass the key argument down to the recursive call, so you're just going to get a TypeError. (I'm guessing your real code doesn't have this problem, but since you didn't show us your real code, or a working example, I can't be sure…)
So:
def Inorder_search_recursive(node, key):
if not node:
return None
result = InOrder_search_recursive(node.lChild, key)
if result is not None:
return result
if node.value==key:
return node
return InOrder_search_recursive(node.rChild, key)
(You don't need the not None check for the right-side search, because if it returns None, we have nothing else to try and are just going to return None anyway.)
My other answer gives the novice-friendly solution, but if you want more powerful and concise answer:
def Inorder_search_recursive_all(node, key):
if not node:
return
yield from InOrder_search_recursive(node.lChild, key)
if node.value==key:
yield node
yield from InOrder_search_recursive(node.rChild, key)
This generates all matches in the tree, in order. And it gives them to you as an iterator, so if you just want the first, you can stop as soon as you find one, with no wasted work:
def Inorder_search_recursive(node, key):
return next(Inorder_search_recursive_all(node, key), None)
The tutorial section on Iterators and the following section on Generators explains most of how this works. The only missing bit is an explanation of yield from, which is explained in PEP 380.
Since your problem is to find the first occurrence node in its inorder traversal, you should 1) traverse the tree in-order and 2) stop when you find the first occurrence.
def search(node, key):
if node is None:
return None
# Search the left subtree and return early if key is found
n = search(node.lChild, key)
if n is not None:
return n
# Check middle and return early if key is found
if node.value == key:
return node
# Search right subtree
return search(node.rChild, key)

Alternative to "assign to a function call" in a python

I'm trying to solve this newbie puzzle:
I've created this function:
def bucket_loop(htable, key):
bucket = hashtable_get_bucket(htable, key)
for entry in bucket:
if entry[0] == key:
return entry[1]
return None
And I have to call it in two other functions (bellow) in the following way: to change the value of the element entry[1] or to append to this list (entry) a new element. But I can't do that calling the function bucket_loop the way I did because "you can't assign to function call" (assigning to a function call is illegal in Python). What is the alternative (most similar to the code I wrote) to do this (bucket_loop(htable, key) = value and hashtable_get_bucket(htable, key).append([key, value]))?
def hashtable_update(htable, key, value):
if bucket_loop(htable, key) != None:
bucket_loop(htable, key) = value
else:
hashtable_get_bucket(htable, key).append([key, value])
def hashtable_lookup(htable, key):
return bucket_loop(htable, key)
Thanks, in advance, for any help!
This is the rest of the code to make this script works:
def make_hashtable(size):
table = []
for unused in range(0, size):
table.append([])
return table
def hash_string(s, size):
h = 0
for c in s:
h = h + ord(c)
return h % size
def hashtable_get_bucket(htable, key):
return htable[hash_string(key, len(htable))]
Similar question (but didn't help me): SyntaxError: "can't assign to function call"
In general, there are three things you can do:
Write “setter” functions (ex, bucket_set)
Return mutable values (ex, bucket_get(table, key).append(42) if the value is a list)
Use a class which overrides __getitem__ and __setitem__
For example, you could have a class like like:
class Bucket(object):
def __setitem__(self, key, value):
# … implementation …
def __getitem__(self, key):
# … implementation …
return value
Then use it like this:
>>> b = Bucket()
>>> b["foo"] = 42
>>> b["foo"]
42
>>>
This would be the most Pythonic way to do it.
One option that would require few changes would be adding a third argument to bucket_loop, optional, to use for assignment:
empty = object() # An object that's guaranteed not to be in your htable
def bucket_loop(htable, key, value=empty):
bucket = hashtable_get_bucket(htable, key)
for entry in bucket:
if entry[0] == key:
if value is not empty: # Reference (id) comparison
entry[1] = value
return entry[1]
else: # I think this else is unnecessary/buggy
return None
However, a few pointers:
I agree with Ignacio Vazquez-Abrams and David Wolever, a class would be better;
Since a bucket can have more than one key/value pairs, you shouldn't return None if the first entry didn't match your key. Loop through all of them, and only return None in the end; (you can ommit this statement also, the default behavior is to return None)
If your htable doesn't admit None as a value, you can use it instead of empty.
So you're basically cheating at udacity, which is an online cs class / university? Funny part is you couldn't even declare the question properly. Next time cheat thoroughly and paste the two functions you're supposed to simplify and request someone simplify them by creating a third function with the overlapping code within. Doesn't matter anyway because if this is the one you need help in you're likely not doing very well in the class
you were also able to solve the problem without using most of these tools, it was an exercise in understanding how to identify an handle redundancies, NOT efficiency...
Real instructions:
Modify the code for both hashtable_update and hashtable_lookup to have the same behavior they have now, but using fewer lines of code in each procedure.  You should define a new procedure, helper, to help with this.  Your new version should have approximately the same running time as the original version, but neither
hashtable_update or hashtable_lookup should include any for or while loop, and the block of each procedure should be no more than 6 lines of code
Seriously, cheating is lame.

Categories