380. Insert Delete GetRandom O(1)
Last Updated: 2020.06.12
Question Source: https://leetcode.com/problems/insert-delete-getrandom-o1/
Let's review what data structures can help us achieve O(1) runtime for each operation required by the question below:
insert(): add a value to data structure
remove(): remove a value from data structure
getRandom: basically the question wants us to do random access in constant time.
The data structure that allows random access is an Array, and in Python, Lists are really Dynamic Arrays.
So we know that we have to use Lists, but lists don't give us constant time to insert or remove elements (except at the very end, using append()
and pop()
).
In this case, we can use another data structure in combination with the List - the dictionary.
The reason we choose a Dictionary is because we need to relate this Dictionary to the List. We'll use the Dictionary for constant time insert()
and remove()
but the List for getRandom()
.
We need the Dictionary and the List to stay in sync, so that if insert()
is called, it inserts into both in constant time, and if remove()
is called, it removes from both in constant time.
The tricky bit is remove()
because we can del
a key from a Dictionary in constant time, but we only pop()
the last element of a List. In this case, we need the Dictionary and the List to work together.
This is where the Dictionary being able to store key-value pairs becomes helpful. We can store the index of the List element in the Dictionary, so that we always have a relationship between the Dictionary and the List.
Example:
dic = { array = [3,2,5,7,4]
3:0,
2:1,
5:2,
7:3,
4:4
}
So we can see that the dictionary stores the index value for each element in the List.
Let's see how the Dictionary and List works with each of the operations required by the question.
dic = {} array = []
insert(3)
actually results in two operations:
array.append(3)
dic[3] = len(array) -1
dic = { array = [3]
3:0
}
We know the length of the List at any time. When we append 3
to the list it goes to the end, so its index will be len(array)-1
. We can store the index in the dictionary as the value for key 3
.
This one gets a little trickier. Say our Dictionary and List looks like this:
dic = { array = [3,2,5,7,4]
3:0,
2:1,
5:2,
7:3,
4:4
}
remove(7)
will result in these operations:
idx = dic[7]
. In this case, idx = 3
To get rid of 7 in the array, simply replace its value with the last element:
last_val = array[-1]
. In this case, last_val = 4
array[3] = 4
'dic = { array = [3,2,5,4,4]
3:0, ^
2:1, value has changed
5:2,
7:3,
4:4
}
Now we can just get rid of the last element, since its redundant: array.pop()
dic = { array = [3,2,5,4]
3:0, ^
2:1, the extra 4 is gone
5:2,
7:3,
4:4
}
But now, we must update the index of the last element in the dictionary: dic[4] = 3
dic = { array = [3,2,5,4]
3:0,
2:1,
5:2,
7:3,
4:3 <- value is updated to the new index
}
Finally, delete 7 from the dictionary: del dic[7]
dic = { array = [3,2,5,4]
3:0,
2:1,
5:2,
4:3 <- the key 7 is deleted
}
Voila! Our Dictionary and List are now in-sync.
Since the indexes for our List start from 0
and end at len(array)-1
we can generate a random number r
in that range.
We can do this in two ways:
r = math.floor(random.random()*(len(array)))
random.random()
returns a random float between [0,1). Ie: 0.656345293423123...By using math.floor() we can round down all the float values to a value between [0,3].
r = random.randint(0,len(array)-1)
Then, we'll return the value for r
with return array[r]
.
import random
class RandomizedSet:
def __init__(self):
"""
Initialize your data structure here.
"""
self.dic = {}
self.array = []
def insert(self, val: int) -> bool:
"""
Inserts a value to the set. Returns true if the set did not already contain the specified element.
"""
if val in self.dic:
return False
self.array.append(val)
self.dic[val] = len(self.array)-1
# print(self.dic)
# print(self.array)
return True
def remove(self, val: int) -> bool:
"""
Removes a value from the set. Returns true if the set contained the specified element.
"""
if val not in self.dic:
return False
last_val = self.array
idx = self.dic[val]
self.array[idx] = last_val
self.dic[last_val] = idx
self.array.pop()
del self.dic[val]
# print(self.dic)
# print(self.array)
return True
def getRandom(self) -> int:
"""
Get a random element from the set.
"""
r = random.randint(0,len(self.array)-1)
return self.array[r]
obj = RandomizedSet()
action = ["RandomizedSet","insert","insert","remove","insert","remove","getRandom"]
val = [[],[0],[1],[0],[2],[1],[]]
for i in range(len(action)):
if action[i] == "insert":
obj.insert(val[i][0])
elif action[i] == "remove":
obj.remove(val[i][0])
elif action[i] == "getRandom":
print(obj.getRandom())