-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathinsert-delete-getrandom-o1.py
133 lines (118 loc) · 3.8 KB
/
insert-delete-getrandom-o1.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
129
130
131
132
133
# 380. Insert Delete GetRandom O(1)
# 🟠 Medium
#
# https://leetcode.com/problems/insert-delete-getrandom-o1/
#
# Tags: Array - Hash Table - Math - Design - Randomized
import random
import timeit
# The naive O(n) for getRandom solution passes the tests.
#
# Runtime: 622 ms, faster than 78.94%
# Memory Usage: 61 MB, less than 93.94%
class RandomizedSetOn:
def __init__(self):
self.s = set()
# O(1)
def insert(self, val: int) -> bool:
if val in self.s:
return False
self.s.add(val)
return True
# O(1)
def remove(self, val: int) -> bool:
if val in self.s:
self.s.remove(val)
return True
return False
# O(n) - The entire set gets converted to a tuple.
def getRandom(self) -> int:
if self.s:
return random.choice(tuple(self.s))
# We can use a list to provide random access to elements by index and a
# hashmap of element value: list index to provide O(1) removals,
# insertion is O(1) for the hashmap and amortized O(1) for the list.
#
# Time complexity: O(1) - All methods.
# Space complexity: O(n) - We keep all elements in two data structures.
#
# Runtime: 1169 ms, faster than 34.30%
# Memory Usage: 61.2 MB, less than 85.69%
class RandomizedSetO1:
def __init__(self):
self.s = {}
self.l = []
# O(1)
def insert(self, val: int) -> bool:
# If the element is in the set, skip insertion.
if val in self.s:
return False
# Append the element to the list and its index to the hashmap
# indexed by value.
self.l.append(val)
self.s[val] = len(self.l) - 1
return True
# O(1)
def remove(self, val: int) -> bool:
if val not in self.s:
return False
# If this element is the last element of the list, pop it.
if self.s[val] == len(self.l) - 1:
self.l.pop()
self.s.pop(val)
else:
# If val is not the last element, move the last element to
# its position. O(1).
last, idx = self.l.pop(), self.s[val]
self.l[idx], self.s[last] = last, idx
self.s.pop(val)
return True
# O(1)
def getRandom(self) -> int:
return random.choice(self.l)
def test():
executors = [
RandomizedSetOn,
RandomizedSetO1,
]
tests = [
[
[
"RandomizedSet",
"insert",
"remove",
"insert",
"getRandom",
"remove",
"insert",
"getRandom",
],
[[], [1], [2], [2], [], [1], [2], []],
[None, True, False, True, 2, True, False, 2],
],
]
for executor in executors:
start = timeit.default_timer()
for _ in range(1):
for col, t in enumerate(tests):
sol = executor()
for i in range(1, len(t[0])):
call = t[0][i]
if call == "getRandom":
# Skip testing get random.
result = getattr(sol, call)()
else:
argument = t[1][i][0]
result = getattr(sol, call)(argument)
exp = t[2][i]
assert result == exp, (
f"\033[93m» {result} <> {exp}\033[91m for"
+ f" test {col}.{i} {call}({argument}) using "
+ f"\033[1m{executor.__name__}"
)
stop = timeit.default_timer()
used = str(round(stop - start, 5))
cols = "{0:20}{1:10}{2:10}"
res = cols.format(executor.__name__, used, "seconds")
print(f"\033[92m» {res}\033[0m")
test()