1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
| class Array(): def __init__(self, size=32, init=None): self._size = size self._items = [init] * size
def __getitem__(self, index): return self._items[index]
def __setitem__(self, index, value): self._items[index] = value
def __len__(self): return self._size
def clear(self, value=None): self._items = [value] * len(self._items)
def __iter__(self): for item in self._items: yield item
class Slot(): def __init__(self, key, value): self.key, self.value = key, value
class HashTable(): UNUSED = None EMPTY = Slot(None, None)
def __init__(self): self._table = Array(8, init=HashTable.UNUSED) self.length = 0
@property def _load_factor(self): return self.length / float(len(self._table))
def __len__(self): return self.length
def _hash(self, key): return abs(hash(key)) % len(self._table)
def _find_key(self, key): index = self._hash(key) _len = len(self._table) while self._table[index] is not HashTable.UNUSED: if self._table[index] is HashTable.EMPTY: index = (index * 5 + 1) % _len continue elif self._table[index].key == key: return index else: index = (index * 5 + 1) % _len return None
def _slot_can_insert(self, index): return (self._table[index] is HashTable.EMPTY or self._table[index] is HashTable.UNUSED)
def _find_slot_for_insert(self, key): index = self._hash(key) _len = len(self._table) while not self._slot_can_insert(index): index = (index * 5 + 1) % _len return index
def __contains__(self, key): index = self._find_key(key) return index is not None
def add(self, key, value): if key in self: index = self._find_key(key) self._table[index].value = value return False else: index = self._find_slot_for_insert(key) self._table[index] = Slot(key, value) self.length += 1 if self._load_factor >= 0.8: self._rehash() return True
def _rehash(self): old_table = self._table newsize = len(self._table) * 2 self._table = Array(newsize, HashTable.UNUSED) self.length = 0
for slot in old_table: if slot is not HashTable.UNUSED and slot is not HashTable.EMPTY: index = self._find_slot_for_insert(slot.key) self._table[index] = slot self.length += 1
def get(self, key, default=None): index = self._find_key(key) if index is None: return default else: return self._table[index].value
def remove(self, key): index = self._find_key(key) if index is None: raise KeyError() value = self._table[index].value self.length -= 1 self._table[index] = HashTable.EMPTY return value
def __iter__(self): for slot in self._table: if slot not in (HashTable.EMPTY, HashTable.UNUSED): yield slot.key
class SetADT(HashTable):
def add(self, key): return super(SetADT, self).add(key, True)
def __and__(self, other_set): new_set = SetADT() for element_a in self: if element_a in other_set: new_set.add(element_a) return new_set
def __sub__(self, other_set): new_set = SetADT() for element_a in self: if element_a not in other_set: new_set.add(element_a) return new_set
def __or__(self, other_set): new_set = SetADT() for element_a in self: new_set.add(element_a) for element_b in other_set: new_set.add(element_b) return new_set
def __xor__(self, other_set): return (self | other_set) - (self & other_set)
def test(): import pytest
sa = SetADT() sa.add(1) sa.add(2) sa.add(3) assert 1 in sa
sb = SetADT() sb.add(3) sb.add(4) sb.add(5)
assert sorted(list(sa & sb)) == [3] assert sorted(list(sa - sb)) == [1, 2] assert sorted(list(sa | sb)) == [1, 2, 3, 4, 5] assert sorted(list(sa ^ sb)) == [1, 2, 4, 5]
|