Implement the RandomizedSet class:
RandomizedSet() Initializes theÂRandomizedSet object.bool insert(int val) Inserts an itemÂval into the set if not present. ReturnsÂtrue if the item was not present,Âfalse otherwise.bool remove(int val) Removes an itemÂval from the set if present. ReturnsÂtrue if the item was present,Âfalse otherwise.int getRandom() Returns a random element from the current set of elements (it’s guaranteed that at least one element exists when this method is called). Each element must have the same probability of being returned.
You must implement the functions of the class such that each function works in average O(1) time complexity.
solutions
naive approach w/ set
We just keep a set, but the conversion to a list in getRandom is .
class RandomizedSet:
def __init__(self):
self.set = set()
def insert(self, val: int) -> bool:
if val in self.set:
return False
self.set.add(val)
return True
def remove(self, val: int) -> bool:
if val in self.set:
self.set.remove(val)
return True
return False
# this doesn't work because it's O(n)!
def getRandom(self) -> int:
return random.choice(list(self.set))array + hashmap w/ constant time deletion
We manually keep track of our values in a pseudo-set by combining a hashmap and an array.
This way, we don’t have to convert our set to an array everytime getRandom is called.
The main trick that allows us to do this while keeping remove constant time is by taking advantage of the fact that we don’t need to maintain any order in our list. Because of this, we can swap the value we want to remove with the value at the top of the list (we know where the value is due to our hashmap), and then just run self.data.pop() in constant time.
class RandomizedSet:
def __init__(self):
# { val: index_of_val_in_data }
self.map = {}
self.data = []
def insert(self, val: int) -> bool:
if val in self.map:
return False
self.data.append(val)
self.map[val] = len(self.data)-1
return True
def remove(self, val: int) -> bool:
if val in self.map:
if len(self.data) == 1:
self.data = []
self.map.clear()
return True
idx_of_val = self.map[val]
last_element = self.data[-1]
# update idx of last_element in map
self.map[last_element] = idx_of_val
# swap val and last_element
self.data[-1], self.data[idx_of_val] = self.data[idx_of_val], self.data[-1]
# remove val from map
self.map.pop(val)
# remove val from data
self.data.pop()
return True
return Falsedoubly-linked list w/ hashmap
I’m just going to leave this here because I managed to pass the test cases with this version, even though getRandom is technically . Maybe it’s because the insertion and deletion is fast with a DLL? Who knows, but probably don’t do this during an interview.
class DLLNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
class RandomizedSet:
def __init__(self):
self.head = DLLNode(None)
self.tail = DLLNode(None)
self.head.left = None
self.head.right = self.tail
self.tail.left = self.head
self.tail.right = None
self.nodes = {}
def insert(self, val: int) -> bool:
if val in self.nodes:
return False
new_node = DLLNode(val)
old_head_right = self.head.right
self.head.right = new_node
new_node.right = old_head_right
old_head_right.left = new_node
new_node.left = self.head
self.nodes[val] = new_node
return True
def remove(self, val: int) -> bool:
if val not in self.nodes:
return False
node_to_remove = self.nodes[val]
node_to_remove.left.right = node_to_remove.right
node_to_remove.right.left = node_to_remove.left
del self.nodes[val]
return True
def getRandom(self) -> int:
vals = self.nodes.keys()
return random.choice(list(vals))