The "Insert Delete GetRandom O(1)" problem asks you to design a data structure that supports three operations with average time complexity of O(1):
insert(val)
: Inserts an item val
into the set if not already present. Returns true
if the item was not present, false
otherwise.remove(val)
: Removes an item val
from the set if present. Returns true
if the item was present, false
otherwise.getRandom()
: Returns a random element from the current set of elements. Each element must have the same probability of being returned.The key constraints are:
At first glance, we might consider using a standard data structure like a set or a list. However, each structure has its limitations:
We realize that if we keep an array (list) for fast random access, and a hash map (dictionary) for fast lookup and deletion, we can achieve our goal. But, we need a trick to remove arbitrary elements from the array in O(1) time.
We use two main data structures:
getRandom()
.The challenge is O(1) removal from the array. To do this:
Breakdown of operations:
Let's walk through a sample sequence of operations:
At every step, all operations run in O(1) time due to the combination of array and hash map.
Brute-force approach:
insert
is O(1), but remove
is O(n), since we need to search for the element.getRandom
is O(1) since we can pick a random index.insert
: O(1) average, as hash map lookup and list append are O(1).remove
: O(1) average, since we swap and pop.getRandom
: O(1), random index selection and array access.The use of both data structures ensures all operations meet the O(1) requirement.
This problem is a classic example of combining two data structures—a dynamic array and a hash map—to leverage their individual strengths. The key trick is swapping the element to be removed with the last element in the array to allow O(1) deletion. This enables us to support insertion, deletion, and random access all in constant time, which would not be possible with a single standard data structure. The solution is elegant, efficient, and highlights the importance of creative data structure design.
import random
class RandomizedSet:
def __init__(self):
self.val_to_idx = {}
self.values = []
def insert(self, val: int) -> bool:
if val in self.val_to_idx:
return False
self.values.append(val)
self.val_to_idx[val] = len(self.values) - 1
return True
def remove(self, val: int) -> bool:
if val not in self.val_to_idx:
return False
idx = self.val_to_idx[val]
last_val = self.values[-1]
self.values[idx] = last_val
self.val_to_idx[last_val] = idx
self.values.pop()
del self.val_to_idx[val]
return True
def getRandom(self) -> int:
return random.choice(self.values)
#include <vector>
#include <unordered_map>
#include <cstdlib>
class RandomizedSet {
public:
std::vector<int> values;
std::unordered_map<int, int> val_to_idx;
RandomizedSet() {}
bool insert(int val) {
if (val_to_idx.count(val)) return false;
values.push_back(val);
val_to_idx[val] = values.size() - 1;
return true;
}
bool remove(int val) {
if (!val_to_idx.count(val)) return false;
int idx = val_to_idx[val];
int last_val = values.back();
values[idx] = last_val;
val_to_idx[last_val] = idx;
values.pop_back();
val_to_idx.erase(val);
return true;
}
int getRandom() {
int idx = rand() % values.size();
return values[idx];
}
};
import java.util.*;
class RandomizedSet {
private List<Integer> values;
private Map<Integer, Integer> valToIdx;
private Random rand;
public RandomizedSet() {
values = new ArrayList<>();
valToIdx = new HashMap<>();
rand = new Random();
}
public boolean insert(int val) {
if (valToIdx.containsKey(val)) return false;
values.add(val);
valToIdx.put(val, values.size() - 1);
return true;
}
public boolean remove(int val) {
if (!valToIdx.containsKey(val)) return false;
int idx = valToIdx.get(val);
int lastVal = values.get(values.size() - 1);
values.set(idx, lastVal);
valToIdx.put(lastVal, idx);
values.remove(values.size() - 1);
valToIdx.remove(val);
return true;
}
public int getRandom() {
int idx = rand.nextInt(values.size());
return values.get(idx);
}
}
class RandomizedSet {
constructor() {
this.values = [];
this.valToIdx = new Map();
}
insert(val) {
if (this.valToIdx.has(val)) return false;
this.values.push(val);
this.valToIdx.set(val, this.values.length - 1);
return true;
}
remove(val) {
if (!this.valToIdx.has(val)) return false;
let idx = this.valToIdx.get(val);
let lastVal = this.values[this.values.length - 1];
this.values[idx] = lastVal;
this.valToIdx.set(lastVal, idx);
this.values.pop();
this.valToIdx.delete(val);
return true;
}
getRandom() {
let idx = Math.floor(Math.random() * this.values.length);
return this.values[idx];
}
}