Hash table and the last value of a key - python

I have a class of hash table. method 'add' adds key and a value. And when I add another value for the same key I would like to replace an old value on a new one. But I don't know what I have to change:)
class HashNode:
def __init__(self, key, value):
self.next = None
self.key = key
self.value = value
class HashTable:
def __init__(self):
self.table = [None] * 1000
def hash(self, key):
hashed = 0
for i in range(len(key)):
hashed = (256 * hashed + ord(key[i])) % 1000
return hashed
def add(self, key, value):
bucket = self.hash(key)
if self.table[bucket]:
temp = self.table[bucket]
while temp.next:
temp = temp.next
temp.next = HashNode(key, value)
else:
self.table[bucket] = HashNode(key, value)
def find(self, key):
bucket = self.hash(key)
if not self.table[bucket]:
return 'none'
else:
temp = self.table[bucket]
while temp:
if temp.key == key:
return temp.value
temp = temp.next
return 'none'
table = HashTable()
table.add('a', 1)
table.add('a', 2)
I am a getting key value '1' but I want '2'
table.find('a')

To elaborate on #mkrieger1's comment: your question is the exact reason why the buckets are not simple cells and why you store the keys in the buckets. If you had no collision, that is key1 != key2 implies hash(key1) != hash(key2)1, you wouln't need to store keys:
def add(self, key, value):
bucket = self.hash(key)
self.table[bucket] = value
def find(self, key, value):
bucket = self.hash(key)
return self.table[bucket]
But you might have collisions. That's why you are using a linked list to store several (key, value) pairs for the keys having the same hash. You correctly handled the collisions in the find method:
temp = self.table[bucket]
while temp:
if temp.key == key: # key found!
return temp.value # return the value
temp = temp.next
return 'none' # why not None?
You should do the same in the add method:
temp = self.table[bucket]
while temp.next:
if temp.key == key: # key found!
temp.value = value # update the value
return # and return
temp = temp.next
temp.next = HashNode(key, value) # key not found: create the entry
Both methods are now symmetrical.
1In maths terms, hash is injective. That is theoritecally possible assuming some conditions that are rarely met.
Remark: you could take advantage of a method that finds HashNodes:
def _find(self, bucket, key):
temp = self.table[bucket]
while temp:
if temp.key == key:
return temp
temp = temp.next
return None
And insert the new keys at the beginning:
def add(self, key, value):
bucket = self.hash(key)
node = self._find(bucket, key)
if node is None:
self.table[bucket] = HashNode(key, value, self.table[bucket]) # last parameter is next
else:
node.value = value
def find(self, key):
bucket = self.hash(key)
node = self._find(bucket, key)
if node is None:
return None
else:
return node.value
The symmetry is even more visible.

Related

Asking how to insert a new node to BST? Python

I am doing my practice, and the practice request create DSATreeNode and DSABinarySearchTree classes. The DSATreeNode is working fine, but when I use the function of insertRec. The insert function will call the insertRec that store the node in the right place. The problem comes is it is not storing and there is nothing in new node. Also the self._root is None, never insert anything into it.
Here's my code
class DSATreeNode:
def __init__(self, inKey, inValue):
self._key = inKey
self._value = inValue
self._left = self._right = None
class DSABinarySearchTree:
def __init__(self):
self._root = None
def find(self, key):
return self._findRec(key, self._root)
def _findRec(self, key, cur):
value = None
if cur == None: # Base case: not found
raise Exception("Key " + key + " not found")
elif key == cur._key: # Base case: found
value = cur._value
elif key < cur._key: # Go left (recursive)
value = self._findRec(key, cur._left)
else: # Go right(recursive)
value = self._findRec(key, cur._right)
return value
def insert(self, inKey, inValue):
return self.insertRec(inKey, inValue, self._root)
def insertRec(self, key, value, curNode):
createNode = DSATreeNode(key, value)
if curNode == None:
curNode = createNode
elif key < curNode._key:
curNode._left = self.insertRec(key, value, curNode._left)
else:
curNode._right = self.insertRec(key, value, curNode._right)
def getRoot(self):
return self._root._key, self._root._value
A few issues:
_insertRec does not return a node. It should have return curNode as its last statement.
There is no code that assigns a node to self._root. The insert method should assign like this: self._root = self.insertRec(inKey, inValue, self._root).
The error message generated in _findRec assumes that key is a string, but if it isn't, then it will fail. Better do raise Exception("Key " + str(key) + " not found") or raise Exception(f"Key {key} not found")
Not blocking, but _insertRec should not create a node when it still is going to make a recursive call. So move that first line into the first if block:
if curNode == None:
curNode = DSATreeNode(key, value)

Why does my logic below in the assign() method not work: instead of node[0] and node[1] I used node.value[0] and node.value[1]?

from linked_list import Node, LinkedList
from blossom_lib import flower_definitions
class HashMap():
def __init__(self, size):
self.array_size = size
self.array = [LinkedList() for number in range(size)]
def hash(self, key):
return sum(key.encode())
def compress(self, hash_code):
return hash_code % self.array_size
def assign(self, key, value):
array_index=self.compress(self.hash(key))
payload = Node([key, value])
#self.array[array_index] = [key, value]
list_at_array = self.array[array_index]
for node in list_at_array:
if node.value[0] == key: #as node has the above definition of payload = Node([key, value]) it basically says we have a node with value = [key, value]
node.value[1] = value
list_at_array.insert(payload)
def retrieve(self, key):
array_index = self.compress(self.hash(key))
list_at_index = self.array[array_index]
for node in list_at_index:
if node.value[0] == key:
return node.value[1]
else:
return None
if payload[0] == key:
return payload[1]
elif payload[0] == None or payload[0] !=key:
return None
blossom = HashMap(len(flower_definitions))
for flower in flower_definitions:
blossom.assign(flower[0],flower[1])
print(flower_definitions)
Output:
Error: LinkedList does not have a value attribute.
LinkedList is a linked list class that stores different nodes of the Node class i.e. Node(value)
By the node definition, we have the following constructor:
self.value = value
...and the value is the key-value pair i.e. [key,value].
However, it tells me that the item within the linked list (in my opinion item = instances of Node class) does not have a value attribute.
My logic:
list_at_array = self.array[array_index]
...where array_index marks the position in the array where we add the key-value pair. The array is a number of LinkedLists - whose items are nodes - with the size of all the flowers that are stored within flower_definitions. Hence, list_at_array selects the first entry of the selected LinkedList object - by definition a node.
If we now we just decide that we add a key-value pair to the LinkedList called list_at_array then we basically tell the LinkedList object to behave like a normal list?

Why is my helper method not activating recursively?

I have a Binary Search Tree and I am trying to trace recursively in order through the tree and append each key,value to a list. It is only appending the first key,value to the list and not going through the list in order. I pasted my code below, along with the test code I used at the bottom. Any help on how to get past this issue is super appreciated!
class TreeMap:
class Node:
def __init__(self, key, value):
self.key = key
self.value = value
self.left = None
self.right = None
def __init__(self):
self.root = None
self.numsearches = 0
self.numcomparisons = 0
def add(self, newkey, newvalue):
newkey = newkey.lower()
if self.root == None:
self.root = TreeMap.Node(newkey, newvalue)
else:
TreeMap.add_helper(self.root, newkey, newvalue)
def add_helper(thisnode, newkey, newvalue):
if newkey <= thisnode.key:
if thisnode.left == None:
thisnode.left = TreeMap.Node(newkey, newvalue)
else:
TreeMap.add_helper(thisnode.left, newkey, newvalue)
else:
if thisnode.right == None:
thisnode.right = TreeMap.Node(newkey, newvalue)
else:
TreeMap.add_helper(thisnode.right, newkey, newvalue)
def print(self):
TreeMap.print_helper(self.root, 0)
def print_helper(somenode, indentlevel):
if somenode == None:
print(" "*(indentlevel),"---")
return
if not TreeMap.isleaf(somenode):
TreeMap.print_helper(somenode.right, indentlevel + 5)
print(" "*indentlevel + str(somenode.key) + ": " +str(somenode.value))
if not TreeMap.isleaf(somenode):
TreeMap.print_helper(somenode.left, indentlevel + 5)
def isleaf(anode):
return anode.left == None and anode.right == None
def listify(self, whichorder="in"):
'''
Returns a list consisting of all the payloads of the tree. (This returns a plain old Python List.)
The order of the payloads is determined by whichorder, which defaults to inorder.
The other possibilities are "pre" and "post".
If the tree is empty, return the empty list.
'''
assert type(whichorder) is str,"Whichorder is a string, and can only be pre, in or post"
assert whichorder in ["pre","in","post"],"Whichorder is a string, and can only be pre, in or post"
return TreeMap.listify_helper(self.root, whichorder)
def listify_helper(somenode, whichorder):
order_list = []
if somenode == None:
return order_list
elif somenode != None and whichorder == 'in':
TreeMap.listify_helper(somenode.left, 'in')
order_list.append(somenode.key+ '='+somenode.value)
TreeMap.listify_helper(somenode.right, 'in')
return order_list
TEST CODE:
import treemap
translator = treemap.TreeMap()
translator.add("cat", "Katze")
translator.add("bird", "Vogel")
translator.add("dog", "Hund")
translator.add("snake", "IDK")
translator.add("bear", "IDK")
translator.add("octopus", "Tintenfisch")
translator.add("horse", "Pferd")
translator.add("zebra", "IDK")
translator.print()
print("---------------------------------------------------")
print (translator.listify())
The problem is here:
def listify_helper(somenode, whichorder):
order_list = []
This function initialises its own local order_list every time it is invoked. Pass order_list as a parameter instead so that the same list is appended to by each recursive invocation.
Alternatively, append each element of the result of the recursive calls of listify_helper to order_list, although this approach could result in unneeded copying.

Python dictionary not adding subsequent keys after the first

Fairly new to Python and I can not figure this out. I go to add a key to a dictionary and it adds it fine. I can even update that same key with a new value, however when I go to add a second key to the dictionary, it does not add the second key value pair.
class CountedSet:
def __init__(self):
self.data = {}
def __iadd__(self,other):
if isinstance(other,int):
self.data[other] = self.data.get(other, 0) + 1
return self
elif isinstance(other,CountedSet):
#TODO::iterate through second countedSet and update self
return self
def __add__(self,obj):
for key, value in obj.data.items():
if len(self.data) == 0:
self.data[key] = value
elif self.data[key]:
self.data[key] = self.data[key] + value
else:
self.data[key] = value
return self
def __getitem__(self,item):
if item in self.data:
return self.data.get(item)
else:
return None
def __str__(self):
for key, value in self.data.items():
return("{%s,%s}" % (key,value))
a = CountedSet()
a += 17
a += 4
print(a)
This simply outputs {17,1} when I would expect to see {17,1} {4,1}
Your __str__ implementation returns on the first iteration of the for-loop:
def __str__(self):
for key, value in self.data.items():
return("{%s,%s}" % (key,value)) # here
Maybe you want something like:
def __str__(self):
return " ".join([{"{%s,%s}" % (k,v) for k, v in self.data.items()])
Or, without the comprehension:
def __str__(self):
items = []
for key, value in self.data.items():
items.append("{%s,%s}" % (key,value))
return ' '.join(items)

Python: global name "treeInsert" is not defined - recursive

I have the following code:
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def treeInsert(rootnode, key):
if rootnode is None:
return Node(key)
if rootnode.key == key:
return rootnode
elif key < rootnode.key:
rootnode.left = treeInsert(rootnode.left, key)
else:
rootnode.right = treeInsert(rootnode.right, key)
return rootnode
If I now want to use the treeInsert method, I get the following error:
NameError: global name 'treeInsert' is not defined.
treeInsert is not a global name, but a method of the Node class. As written, you should make it a static method, and refer to it as Node.treeInsert.
#staticmethod
def treeInsert(rootnode, key):
if rootnode is None:
return Node(key)
if rootnode.key == key:
return rootnode
elif key < rootnode.key:
rootnode.left = Node.treeInsert(rootnode.left, key)
else:
rootnode.right = Node.treeInsert(rootnode.right, key)
return root node
Better yet, make it a proper instance method, something like
def treeInsert(self, key):
if self.key == key:
return key
elif key < self.key:
return (self.left or Node(key)).treeInsert(key)
else:
return (self.right or Node(key)).treeInsert(key)

Categories