380. Insert Delete GetRandom O(1)
Problem Description
The RandomizedSet
class is designed to perform operations on a collection of unique elements. It allows for the insertion and removal of elements and getting a random element from the set. The class methods which are required to be implemented are as follows:
insert(val)
: Adds theval
to the set if it's not already present, and returnstrue
; ifval
is already in the set, it returnsfalse
.remove(val)
: Removes theval
from the set if it's present and returnstrue
; ifval
is not present in the set, it returnsfalse
.getRandom()
: Returns a random element from the set, ensuring each element has an equal probability of being selected.
The constraint given is that each function should operate in average constant time, i.e., O(1)
time complexity.
Intuition
The challenge lies in achieving O(1)
time complexity for each operation - insert, remove, and getRandom. A standard set data structure wouldn’t suffice for getRandom()
to be O(1)
. For efficient random access, we need to use a list structure where random access is O(1)
. However, a list alone does not provide O(1)
time complexity for insert and remove operations due to potential shifting of elements. To navigate this, we use a combination of a hash table (dictionary in Python) and a dynamic array (list in Python).
For insertion, a dynamic array (list) supports adding an element in O(1)
time. To handle duplicates, we accompany the list with a hash table that stores elements as keys and their respective indices in the list as values. This simultaneously checks for duplicates and maintains the list of elements.
For removals, a list doesn't remove an element in O(1)
time because it might have to shift elements. To circumvent this, we swap the element to be removed with the last element and then pop the last element from the list. This way, the removal doesn't require shifting all subsequent elements. After swapping and before popping, we must update the hash table accordingly to reflect the new index of the element that was swapped.
Getting a random element efficiently is accomplished with a list since we can access elements by an index in O(1)
time. Since all elements in the set have an equal probability of being picked, we can select a random index and return the element at that index from the list.
Overall, the use of both data structures allows us to maintain the average constant time complexity constraint required by the problem for all operations, giving us an efficient solution that meets the problem requirements.
Learn more about Math patterns.
Solution Approach
The solution approach utilizes a blend of data structures and careful bookkeeping to ensure that each operation—insert, remove, and getRandom—executes in average constant O(1)
time complexity.
Algorithms and Data Structures:
- Hash Table/Dictionary (
self.d
): This hash table keeps track of the values as keys and their corresponding indices in the dynamic array as values. The hash table enablesO(1)
access time for checking if a value is already in the set and for removing values by looking up their index. - Dynamic Array/List (
self.q
): The dynamic array stores the elements of the set and allows us to utilize theO(1)
access time to get a random element.
insert
Implementation:
- Check if
val
is already in theself.d
hash table. If it is, returnfalse
because no duplicate values are allowed in the set. - If
val
is not present, addval
as a key toself.d
with the value being the current size of the listself.q
(which will be the index of the inserted value). - Then, append
val
to the listself.q
. - Return
true
because a new value has been successfully inserted into the set.
remove
Implementation:
- Check if
val
is present in theself.d
hash table. If not, returnfalse
because there's nothing to remove. - If
val
is present, locate its indexi
inself.q
usingself.d[val]
. - Swap the value
val
inself.q
with the last element inself.q
to moveval
to the end of the list for O(1) removal. - The hash table
self.d
needs to be updated to reflect the new index for the value that was swapped to the position previously held byval
. - Pop the last element from
self.q
, which is nowval
. - Remove
val
from the hash tableself.d
. - Return
true
because the value has been successfully removed from the set.
getRandom
Implementation:
- Use Python's
choice
function from therandom
module to select a random element from the listself.q
. Thechoice
function inherently operates inO(1)
time complexity because it selects an index randomly and returns the element at that index from the list.
This approach essentially provides an efficient and elegant solution to conduct insert, remove, and getRandom operations on a set with constant average time complexity, fulfilling the problem's constraints using the combination of a hash table with a dynamic array.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
-
Initialize:
- We start by initializing our
RandomizedSet
. Both the dynamic arrayself.q
and the hash tableself.d
are empty.
- We start by initializing our
-
Insert 5:
- Call
insert(5)
. Since 5 is not inself.d
, we add it with its index toself.d
(i.e.,self.d[5] = 0
) and append it toself.q
(i.e.,self.q = [5]
). - The operation returns
true
.
- Call
-
Insert 10:
- Call
insert(10)
. Similarly, since 10 is not inself.d
, we add it with the next index toself.d
(i.e.,self.d[10] = 1
) and append it toself.q
(i.e.,self.q = [5, 10]
). - The operation returns
true
.
- Call
-
Insert 5 again:
- Call
insert(5)
. Since 5 is already inself.d
, we do not add it toself.q
and returnfalse
.
- Call
-
Get a random element:
- Call
getRandom()
. The function could return either 5 or 10, each with a 50% probability.
- Call
-
Remove 5:
- Call
remove(5)
. We find the index of 5 fromself.d
, which is 0. We then swapself.q[0]
(5) with the last element inself.q
(which is 10), resulting inself.q = [10, 5]
. - We update
self.d
to reflect the swap (nowself.d[10] = 0
), pop the last element inself.q
(removing 5), and deleteself.d[5]
. - The operation returns
true
.
- Call
-
Current state:
- The dynamic array
self.q
now holds[10]
, andself.d
holds{10: 0}
.
- The dynamic array
This simple example demonstrates the processes of each operation and how the combination of a hash table and a dynamic array can achieve O(1)
average time complexity for insertions, removals, and accessing a random element.
Solution Implementation
1from random import choice
2
3class RandomizedSet:
4 def __init__(self):
5 self.index_dict = {} # Mapping of values to their indices in the array
6 self.values_list = [] # Dynamic array to hold the values
7
8 def insert(self, val: int) -> bool:
9 # Insert the value into the set if it's not already present, returning True if successful
10 if val in self.index_dict:
11 return False # Value already exists
12 self.index_dict[val] = len(self.values_list) # Map value to its index in the array
13 self.values_list.append(val) # Add value to the array
14 return True # Insertion successful
15
16 def remove(self, val: int) -> bool:
17 # Remove the value from the set if present, returning True if successful
18 if val not in self.index_dict:
19 return False # Value does not exist
20 index_to_remove = self.index_dict[val] # Get the index of the value to remove
21 last_element = self.values_list[-1] # Get the last element in the array
22 self.values_list[index_to_remove] = last_element # Move the last element to the 'removed' position
23 self.index_dict[last_element] = index_to_remove # Update the last element's index
24 self.values_list.pop() # Remove the last element
25 del self.index_dict[val] # Remove the value from the dictionary
26 return True # Removal successful
27
28 def getRandom(self) -> int:
29 # Return a random value from the set
30 return choice(self.values_list) # Randomly select and return a value from the array
31
32# Example usage:
33# randomized_set = RandomizedSet()
34# param_1 = randomized_set.insert(val)
35# param_2 = randomized_set.remove(val)
36# param_3 = randomized_set.getRandom()
37
1import java.util.ArrayList;
2import java.util.HashMap;
3import java.util.List;
4import java.util.Map;
5import java.util.Random;
6
7// RandomizedSet design allows for O(1) time complexity for insertion, deletion and getting a random element.
8class RandomizedSet {
9 private Map<Integer, Integer> valueToIndexMap = new HashMap<>(); // Maps value to its index in 'valuesList'.
10 private List<Integer> valuesList = new ArrayList<>(); // Stores the values.
11 private Random randomGenerator = new Random(); // Random generator for getRandom() method.
12
13 // Constructor of the RandomizedSet.
14 public RandomizedSet() {
15 }
16
17 // Inserts a value to the set. Returns true if the set did not already contain the specified element.
18 public boolean insert(int val) {
19 if (valueToIndexMap.containsKey(val)) {
20 // If the value is already present, return false.
21 return false;
22 }
23 // Map the value to the size of the list which is the future index of this value.
24 valueToIndexMap.put(val, valuesList.size());
25 // Add the value to the end of the values list.
26 valuesList.add(val);
27 return true;
28 }
29
30 // Removes a value from the set. Returns true if the set contained the specified element.
31 public boolean remove(int val) {
32 if (!valueToIndexMap.containsKey(val)) {
33 // If the value is not present, return false.
34 return false;
35 }
36 // Get index of the element to remove.
37 int indexToRemove = valueToIndexMap.get(val);
38 // Get last element in the list.
39 int lastElement = valuesList.get(valuesList.size() - 1);
40 // Move the last element to the place of the element to remove.
41 valuesList.set(indexToRemove, lastElement);
42 // Update the map with the new index of lastElement.
43 valueToIndexMap.put(lastElement, indexToRemove);
44 // Remove the last element from the list.
45 valuesList.remove(valuesList.size() - 1);
46 // Remove the entry for the removed element from the map.
47 valueToIndexMap.remove(val);
48 return true;
49 }
50
51 // Get a random element from the set.
52 public int getRandom() {
53 // Returns a random element using the random generator.
54 return valuesList.get(randomGenerator.nextInt(valuesList.size()));
55 }
56
57 // The below comments describe how your RandomizedSet class could be used:
58 // RandomizedSet obj = new RandomizedSet();
59 // boolean param_1 = obj.insert(val);
60 // boolean param_2 = obj.remove(val);
61 // int param_3 = obj.getRandom();
62}
63
1#include <vector>
2#include <unordered_map>
3#include <cstdlib>
4
5class RandomizedSet {
6public:
7 RandomizedSet() {
8 // Constructor doesn't need to do anything since the vector and
9 // unordered_map are initialized by default
10 }
11
12 // Inserts a value to the set. Returns true if the set did not already contain the specified element
13 bool insert(int val) {
14 if (indexMap.count(val)) {
15 // Value is already in the set, so insertion is not possible
16 return false;
17 }
18 indexMap[val] = values.size(); // Map value to its index in 'values'
19 values.push_back(val); // Add value to the end of 'values'
20 return true;
21 }
22
23 // Removes a value from the set. Returns true if the set contained the specified element
24 bool remove(int val) {
25 if (!indexMap.count(val)) {
26 // Value is not in the set, so removal is not possible
27 return false;
28 }
29
30 int index = indexMap[val]; // Get index of the element to remove
31 indexMap[values.back()] = index; // Map last element's index to the index of the one to be removed
32 values[index] = values.back(); // Replace the element to remove with the last element
33 values.pop_back(); // Remove last element
34 indexMap.erase(val); // Remove element from map
35
36 return true;
37 }
38
39 // Gets a random element from the set
40 int getRandom() {
41 return values[rand() % values.size()]; // Return a random element by index
42 }
43
44private:
45 std::unordered_map<int, int> indexMap; // Maps value to its index in 'values'
46 std::vector<int> values; // Stores the actual values
47};
48
49/**
50 * Your RandomizedSet object will be instantiated and called as such:
51 * RandomizedSet* obj = new RandomizedSet();
52 * bool param_1 = obj->insert(val);
53 * bool param_2 = obj->remove(val);
54 * int param_3 = obj->getRandom();
55 */
56
1// Store the number and its corresponding index in the array
2let valueToIndexMap: Map<number, number> = new Map();
3// Store the numbers for random access
4let valuesArray: number[] = [];
5
6// Inserts a value to the set. Returns true if the set did not already contain the specified element.
7function insert(value: number): boolean {
8 if (valueToIndexMap.has(value)) {
9 // Value already exists, so insertion is not done
10 return false;
11 }
12 // Add value to the map and array
13 valueToIndexMap.set(value, valuesArray.length);
14 valuesArray.push(value);
15 return true;
16}
17
18// Removes a value from the set. Returns true if the set contained the specified element.
19function remove(value: number): boolean {
20 if (!valueToIndexMap.has(value)) {
21 // Value does not exist; hence, nothing to remove
22 return false;
23 }
24 // Get index of the value to be removed
25 const index = valueToIndexMap.get(value)!;
26 // Move the last element to fill the gap of the removed element
27 // Update the index of the last element in the map
28 valueToIndexMap.set(valuesArray[valuesArray.length - 1], index);
29 // Swap the last element with the one at the index
30 valuesArray[index] = valuesArray[valuesArray.length - 1];
31 // Remove the last element
32 valuesArray.pop();
33 // Remove the entry for the removed value from the map
34 valueToIndexMap.delete(value);
35 return true;
36}
37
38// Get a random element from the set.
39function getRandom(): number {
40 // Choose a random index and return the element at that index
41 return valuesArray[Math.floor(Math.random() * valuesArray.length)];
42}
43
44// Usage example:
45// var successInsert = insert(3); // returns true
46// var successRemove = remove(3); // returns true
47// var randomValue = getRandom(); // returns a random value from the set
48
Time and Space Complexity
Time Complexity:
insert(val: int) -> bool
: The insertion function consists of a dictionary check and insertion into a dictionary and a list, which are typicallyO(1)
operations. Thus, the time complexity isO(1)
.remove(val: int) -> bool
: The remove function performs a dictionary check, index retrieval, and element swap in the list, as well as removal from both the dictionary and list. Dictionary and list operations involved here are usuallyO(1)
. Therefore, the time complexity isO(1)
.getRandom() -> int
: ThegetRandom
function makes a single call to thechoice()
function from the Pythonrandom
module, which selects a random element from the list inO(1)
time. Hence, the time complexity isO(1)
.
Overall, each of the operations provided by the RandomizedSet
class have O(1)
time complexity.
Space Complexity:
- The RandomizedSet class maintains a dictionary (
self.d
) and a list (self.q
). The dictionary maps element values to their indices in the list, and both data structures store up ton
elements, wheren
is the total number of unique elements inserted into the set. Hence, the space complexity isO(n)
, accounting for the storage ofn
elements in both the dictionary and the list.
Learn more about how to find time and space complexity quickly using problem constraints.
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!