-
Notifications
You must be signed in to change notification settings - Fork 77
/
sorted_table_map.py
128 lines (107 loc) · 3.87 KB
/
sorted_table_map.py
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
# coding: utf-8
from data_structures.hash_maps.base_map import BaseMap
# This implementation only supports keys of the same type in an instance.
class SortedTableMap(BaseMap):
def __init__(self):
self._table = []
# O(1)
def __len__(self):
return len(self._table)
# O(n)
def __iter__(self):
for item in self._table:
yield item.key
# O(log n)
def _insertable_binary_search(self, target_key, low, high):
# NOTE: If target_key is not in _table,
# it returns the index that the key should be inserted.
if low > high:
# (found, index)
return (False, high + 1)
mid = (low + high) // 2
mid_key = self._table[mid].key
if target_key == mid_key:
# There is no duplicate key in a map,
# so we don't need neither the left or right bound version of Binary Search.
return (True, mid)
elif target_key < mid_key:
return self._insertable_binary_search(target_key, low, mid - 1)
elif target_key > mid_key:
return self._insertable_binary_search(target_key, mid + 1, high)
# O(log n) + O(n)
def __setitem__(self, key, value):
found, index = self._insertable_binary_search(key, 0, len(self._table) - 1)
if found:
self._table[index].value = value
else:
# index might be len(_table) if key is greater than every item in _table.
self._table.insert(index, self.ITEM_CLASS(key, value))
# O(log n)
def __getitem__(self, key):
found, index = self._insertable_binary_search(key, 0, len(self._table) - 1)
if found:
return self._table[index].value
else:
raise KeyError
# O(log n) + O(n)
def __delitem__(self, key):
found, index = self._insertable_binary_search(key, 0, len(self._table) - 1)
if found:
self._table.pop(index)
else:
raise KeyError
# O(1)
def find_min(self):
try:
item = self._table[0]
except IndexError:
return None
else:
return (item.key, item.value)
# O(1)
def find_max(self):
try:
item = self._table[-1]
except IndexError:
return None
else:
return (item.key, item.value)
# O(log n) + O(n)
def find_lt(self, key):
_, index = self._insertable_binary_search(key, 0, len(self._table) - 1)
start, stop = 0, index
for item in self._table[start:stop]:
yield (item.key, item.value)
# O(log n) + O(n)
def find_le(self, key):
found, index = self._insertable_binary_search(key, 0, len(self._table) - 1)
if found:
start, stop = 0, index + 1
else:
start, stop = 0, index
for item in self._table[start:stop]:
yield (item.key, item.value)
# O(log n) + O(n)
def find_gt(self, key):
found, index = self._insertable_binary_search(key, 0, len(self._table) - 1)
if found:
start, stop = index + 1, len(self._table)
else:
start, stop = index, len(self._table)
for item in self._table[start:stop]:
yield (item.key, item.value)
# O(log n) + O(n)
def find_ge(self, key):
_, index = self._insertable_binary_search(key, 0, len(self._table) - 1)
start, stop = index, len(self._table)
for item in self._table[start:stop]:
yield (item.key, item.value)
# O(log n) + O(n)
def find_range(self, start_key, stop_key):
if start_key > stop_key:
return
_, i = self._insertable_binary_search(start_key, 0, len(self._table) - 1)
while i < len(self._table) and (self._table[i].key < stop_key):
item = self._table[i]
yield (item.key, item.value)
i += 1