avp26/u7/hash_table.py

105 lines
3.4 KiB
Python

class HashTable:
_DELETED = object()
def __init__(self, capacity=10):
if type(capacity) != int:
raise TypeError("Capacity must be an integer")
if capacity <= 0:
raise ValueError("Capacity must be positive")
self._capacity = capacity
self._table = [None] * capacity
self._size = 0
@property
def capacity(self):
return self._capacity
def hash(self, key):
if type(key) != str:
raise TypeError
h = 0
for char in key:
h = (31 * h + ord(char)) % self.capacity
return h
def probe(self, key):
if type(key) != str:
raise TypeError
start = self.hash(key)
deleted = None
for i in range(self.capacity):
index = (start + i) % self.capacity
item = self._table[index]
if item is None:
if deleted is not None:
return deleted
return index
if item is self._DELETED:
if deleted is None:
deleted = index
elif item[0] == key:
return index
if deleted is not None:
return deleted
raise RuntimeError
def __setitem__(self, key, value):
if type(key) != str:
raise TypeError
try:
index = self.probe(key)
item = self._table[index]
if item is not None and item is not self._DELETED:
if item[0] == key:
self._table[index] = (key, value)
return
except RuntimeError:
pass
if (self._size + 1) / self.capacity > 0.7:
old_table = self._table
self._capacity = self._capacity * 2
self._table = [None] * self.capacity
self._size = 0
for item in old_table:
if item is not None and item is not self._DELETED:
index = self.probe(item[0])
self._table[index] = item
self._size += 1
index = self.probe(key)
self._table[index] = (key, value)
self._size += 1
def __getitem__(self, key):
if type(key) != str:
raise TypeError
index = self.probe(key)
item = self._table[index]
if item is None or item is self._DELETED:
raise KeyError(key)
return item[1]
def __delitem__(self, key):
if type(key) != str:
raise TypeError
index = self.probe(key)
item = self._table[index]
if item is None or item is self._DELETED:
raise KeyError(key)
self._table[index] = self._DELETED
self._size -= 1
def __contains__(self, key):
if type(key) != str:
raise TypeError
try:
index = self.probe(key)
item = self._table[index]
if item is not None and item is not self._DELETED:
if item[0] == key:
return True
return False
except RuntimeError:
return False
def __len__(self):
return self._size
def __iter__(self):
keys = []
for item in self._table:
if item is not None and item is not self._DELETED:
keys.append(item[0])
return iter(keys)
def load_factor(self):
return self._size / self.capacity