Insert Delete GetRandom O(1) solution and explanation
9 more videos on this problem
Insert Delete GetRandom O(1) - Leetcode 380 - PythonNeetCode · 13:27 · 80K views
Design data structure | Insert Delete GetRandom O(1) | Leetcode #380Techdose · 15:45 · 28K views
Insert Delete GetRandom O(1) | Detailed | Google | Leetcode 380codestorywithMIK · 21:24 · 26K views
Leetcode | 380. Insert Delete GetRandom O(1) | Medium | Java SolutionDeveloper Docs · 9:26 · 6K views
INSERT/DELETE/GETRANDOM() O(1) | PYTHON | LEETCODE # 380Cracking FAANG · 14:38 · 2K views
Insert Delete GetRandom O(1) | LeetCode 380 | Programming TutorialsProgramming Tutorials · 13:23 · 1K views
Leetcode - Insert Delete GetRandom O(1) (Python)Timothy H Chang · 2:44 · 1K views
Insert Delete GetRandom O(1) | Day 12 | [June LeetCoding Challenge] [Leetcode #380] [2020]Algorithms Made Easy · 4:22 · 1K views
Insert Delete GetRandom O(1) | Leetcode 380 | Live coding session 🔥🔥🔥Coding Decoded · 14:14 · 812 views
380. Insert Delete GetRandom O(1) | JavaScript | LeetCode Daily ChallengeLeetCodeWithMonu · 21:44 · 708 views
The problem
Implement the RandomizedSet class:
RandomizedSet()Initializes theRandomizedSetobject.bool insert(int val)Inserts an itemvalinto the set if not present. Returnstrueif the item was not present,falseotherwise.bool remove(int val)Removes an itemvalfrom the set if present. Returnstrueif the item was present,falseotherwise.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.
Example 1:
Input ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"] [[], [1], [2], [2], [], [1], [2], []] Output [null, true, false, true, 2, true, false, 2] Explanation RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); // Inserts 1 to the set. Returns true as 1 was inserted successfully. randomizedSet.remove(2); // Returns false as 2 does not exist in the set. randomizedSet.insert(2); // Inserts 2 to the set, returns true. Set now contains [1,2]. randomizedSet.getRandom(); // getRandom() should return either 1 or 2 randomly. randomizedSet.remove(1); // Removes 1 from the set, returns true. Set now contains [2]. randomizedSet.insert(2); // 2 was already in the set, so return false. randomizedSet.getRandom(); // Since 2 is the only number in the set, getRandom() will always return 2.
Constraints:
-231 <= val <= 231 - 1- At most
2 *105calls will be made toinsert,remove, andgetRandom. - There will be at least one element in the data structure when
getRandomis called.
Approach overview
Problem Overview: Design a data structure that supports insert(val), remove(val), and getRandom() in average O(1) time. The random operation must return each stored element with equal probability.
Approach 1: Divide and Conquer Approach (Array + Hash Map) (Time: O(1), Space: O(n))
The key idea is combining a dynamic array with a hash map. The array stores elements so you can pick a random value in constant time using a random index. The hash map stores value → index so you can locate elements instantly during deletion. When removing a value, swap it with the last element in the array, update the hash map for the swapped value, and then pop the last element. This avoids expensive shifting operations and keeps deletion O(1). Hash lookups and updates provide constant time access, which makes all three operations efficient. This design heavily relies on concepts from Hash Table and Array data structures.
Approach 2: Iterative Approach using Heap Sort (Time: O(log n), Space: O(n))
A less optimal design stores values inside a heap-like structure where insertions and deletions follow heap operations. Insert pushes the element and rebalances the heap in O(log n). Removal searches and restructures the heap afterward. Random selection can be simulated by choosing a random index from the heap array representation, but heap adjustments during updates increase the complexity. While workable, this structure sacrifices the constant-time requirement and mainly demonstrates how iterative heap-based structures manage dynamic collections. Concepts overlap with Design and randomized data structures.
Recommended for interviews: Interviewers expect the array + hash map design. It demonstrates understanding of constant-time hash lookups and how swapping with the last array element avoids costly deletions. Discussing the heap-based or naive structures first shows awareness of tradeoffs, but the hash map + array combination is the standard optimal solution.
Approach 1: Divide and Conquer Approach
This approach breaks down the problem into smaller subproblems. We solve the subproblems recursively and then combine their solutions to solve the original problem. This is useful in problems like merge sort or quicksort.
def mergeSort(arr):
if len(arr) > 1:
mid = len(arr) // 2
L = arr[:mid]
R = arr[mid:]
mergeSort(L)
mergeSort(R)
i = j = k = 0
while i < len(L) and j < len(R):
if L[i] < R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
while i < len(L):
arr[k] = L[i]
i += 1
k += 1
while j < len(R):
arr[k] = R[j]
j += 1
k += 1
arr = [12, 11, 13, 5, 6, 7]
mergeSort(arr)
print(arr)public class MergeSort {
public static void merge(int arr[], int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int L[] = new int[n1];
int R[] = new int[n2];
for (int i = 0; i < n1; ++i)
L[i] = arr[l + i];
for (int j = 0; j < n2; ++j)
R[j] = arr[m + 1 + j];
int i = 0, j = 0;
int k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1)
arr[k++] = L[i++];
while (j < n2)
arr[k++] = R[j++];
}
public static void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = (l + r) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
public static void main(String args[]) {
int arr[] = {12, 11, 13, 5, 6, 7};
mergeSort(arr, 0, arr.length - 1);
for (int i = 0; i < arr.length; ++i)
System.out.print(arr[i] + " ");
}
}#include <iostream>
using namespace std;
void merge(int arr[], int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int L[n1], R[n2];
for (int i = 0; i < n1; i++)
L[i] = arr[l + i];
for (int j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];
int i = 0, j = 0, k = l;
while (i < n1 && j < n2) {
arr[k++] = L[i] <= R[j] ? L[i++] : R[j++];
}
while (i < n1)
arr[k++] = L[i++];
while (j < n2)
arr[k++] = R[j++];
}
void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int arr_size = sizeof(arr) / sizeof(arr[0]);
mergeSort(arr, 0, arr_size - 1);
for (int i = 0; i < arr_size; i++)
cout << arr[i] << " ";
return 0;
}function mergeSort(arr) {
if (arr.length > 1) {
const mid = Math.floor(arr.length / 2);
const L = arr.slice(0, mid);
const R = arr.slice(mid);
mergeSort(L);
mergeSort(R);
let i = 0, j = 0, k = 0;
while (i < L.length && j < R.length) {
if (L[i] < R[j]) {
arr[k++] = L[i++];
} else {
arr[k++] = R[j++];
}
}
while (i < L.length) arr[k++] = L[i++];
while (j < R.length) arr[k++] = R[j++];
}
return arr;
}
const arr = [12, 11, 13, 5, 6, 7];
console.log(mergeSort(arr));#include <stdio.h>
void merge(int arr[], int l, int m, int r) {
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
int L[n1], R[n2];
for (i = 0; i < n1; i++)
L[i] = arr[l + i];
for (j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];
i = 0;
j = 0;
k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
}
else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int arr_size = sizeof(arr) / sizeof(arr[0]);
mergeSort(arr, 0, arr_size - 1);
for (int i = 0; i < arr_size; i++)
printf("%d ", arr[i]);
return 0;
}using System;
class MergeSort {
public static void Merge(int[] arr, int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int[] L = new int[n1];
int[] R = new int[n2];
Array.Copy(arr, l, L, 0, n1);
Array.Copy(arr, m + 1, R, 0, n2);
int i = 0, j = 0, k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k++] = L[i++];
} else {
arr[k++] = R[j++];
}
}
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
}
public static void MergeSortRec(int[] arr, int l, int r) {
if (l < r) {
int m = (l + r) / 2;
MergeSortRec(arr, l, m);
MergeSortRec(arr, m + 1, r);
Merge(arr, l, m, r);
}
}
static void Main() {
int[] arr = {12, 11, 13, 5, 6, 7};
MergeSortRec(arr, 0, arr.Length - 1);
Console.WriteLine(string.Join(" ", arr));
}
}This Python code for merge sort divides the list into halves recursively, sorts each half, and then merges them back together in a sorted manner.
Time Complexity: O(n log n)
Space Complexity: O(n)
Approach 2: Iterative Approach using Heap Sort
This approach uses a binary heap data structure to sort the elements. Unlike the recursive nature of merge sort, heap sort uses an iterative process to build a max heap and then extracts the maximum element one by one.
def heapify(arr, n, i):
largest = i
l = 2 * i + 1
r = 2 * i + 2
if l < n and arr[i] < arr[l]:
largest = l
if r < n and arr[largest] < arr[r]:
largest = r
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def heapSort(arr):
n = len(arr)
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i]
heapify(arr, i, 0)
arr = [12, 11, 13, 5, 6, 7]
heapSort(arr)
print(arr)public class HeapSort {
public void heapify(int arr[], int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest])
largest = l;
if (r < n && arr[r] > arr[largest])
largest = r;
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
heapify(arr, n, largest);
}
}
public void sort(int arr[]) {
int n = arr.length;
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
for (int i = n - 1; i >= 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapify(arr, i, 0);
}
}
public static void main(String args[]) {
int arr[] = {12, 11, 13, 5, 6, 7};
HeapSort hs = new HeapSort();
hs.sort(arr);
for (int i : arr)
System.out.print(i + " ");
}
}#include <iostream>
using namespace std;
void heapify(int arr[], int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest])
largest = l;
if (r < n && arr[r] > arr[largest])
largest = r;
if (largest != i) {
swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
void heapSort(int arr[], int n) {
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
for (int i = n - 1; i >= 0; i--) {
swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int n = sizeof(arr) / sizeof(arr[0]);
heapSort(arr, n);
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
return 0;
}function heapify(arr, n, i) {
let largest = i;
const l = 2 * i + 1;
const r = 2 * i + 2;
if (l < n && arr[l] > arr[largest])
largest = l;
if (r < n && arr[r] > arr[largest])
largest = r;
if (largest != i) {
[arr[i], arr[largest]] = [arr[largest], arr[i]];
heapify(arr, n, largest);
}
}
function heapSort(arr) {
const n = arr.length;
for (let i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
for (let i = n - 1; i > 0; i--) {
[arr[0], arr[i]] = [arr[i], arr[0]];
heapify(arr, i, 0);
}
return arr;
}
const arr = [12, 11, 13, 5, 6, 7];
console.log(heapSort(arr));#include <stdio.h>
void heapify(int arr[], int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest])
largest = l;
if (r < n && arr[r] > arr[largest])
largest = r;
if (largest != i) {
int temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
heapify(arr, n, largest);
}
}
void heapSort(int arr[], int n) {
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
for (int i = n - 1; i >= 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapify(arr, i, 0);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int n = sizeof(arr) / sizeof(arr[0]);
heapSort(arr, n);
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
return 0;
}using System;
class HeapSort {
public void Heapify(int[] arr, int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest])
largest = l;
if (r < n && arr[r] > arr[largest])
largest = r;
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
Heapify(arr, n, largest);
}
}
public void Sort(int[] arr) {
int n = arr.Length;
for (int i = n / 2 - 1; i >= 0; i--)
Heapify(arr, n, i);
for (int i = n - 1; i >= 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
Heapify(arr, i, 0);
}
}
static void Main() {
int[] arr = {12, 11, 13, 5, 6, 7};
HeapSort hs = new HeapSort();
hs.Sort(arr);
Console.WriteLine(string.Join(" ", arr));
}
}This Python function for heap sort constructs a heap and sorts the array by continuously removing the largest element.
Time Complexity: O(n log n)
Space Complexity: O(1)
Approach 3: Hash Table + Dynamic List
We define a dynamic list q to store the elements in the set, and a hash table d to store the index of each element in q.
When inserting an element, if the element already exists in the hash table d, return false directly; otherwise, we insert the element into the end of the dynamic list q, and insert the element and its index in q into the hash table d at the same time, and finally return true.
When deleting an element, if the element does not exist in the hash table d, return false directly; otherwise, we obtain the index of the element in the list q from the hash table, then swap the last element q[-1] in the list q with q[i], and then update the index of q[-1] in the hash table to i. Then delete the last element in q, and remove the element from the hash table at the same time, and finally return true.
When getting a random element, we can randomly select an element from the dynamic list q and return it.
Time complexity O(1), space complexity O(n), where n is the number of elements in the set.
class RandomizedSet {
private d: Map<number, number> = new Map();
private q: number[] = [];
constructor() {}
insert(val: number): boolean {
if (this.d.has(val)) {
return false;
}
this.d.set(val, this.q.length);
this.q.push(val);
return true;
}
remove(val: number): boolean {
if (!this.d.has(val)) {
return false;
}
const i = this.d.get(val)!;
this.d.set(this.q[this.q.length - 1], i);
this.q[i] = this.q[this.q.length - 1];
this.q.pop();
this.d.delete(val);
return true;
}
getRandom(): number {
return this.q[Math.floor(Math.random() * this.q.length)];
}
}
/**
* Your RandomizedSet object will be instantiated and called as such:
* var obj = new RandomizedSet()
* var param_1 = obj.insert(val)
* var param_2 = obj.remove(val)
* var param_3 = obj.getRandom()
*/type RandomizedSet struct {
d map[int]int
q []int
}
func Constructor() RandomizedSet {
return RandomizedSet{map[int]int{}, []int{}}
}
func (this *RandomizedSet) Insert(val int) bool {
if _, ok := this.d[val]; ok {
return false
}
this.d[val] = len(this.q)
this.q = append(this.q, val)
return true
}
func (this *RandomizedSet) Remove(val int) bool {
if _, ok := this.d[val]; !ok {
return false
}
i := this.d[val]
this.d[this.q[len(this.q)-1]] = i
this.q[i] = this.q[len(this.q)-1]
this.q = this.q[:len(this.q)-1]
delete(this.d, val)
return true
}
func (this *RandomizedSet) GetRandom() int {
return this.q[rand.Intn(len(this.q))]
}
/**
* Your RandomizedSet object will be instantiated and called as such:
* obj := Constructor();
* param_1 := obj.Insert(val);
* param_2 := obj.Remove(val);
* param_3 := obj.GetRandom();
*/use rand::Rng;
use std::collections::HashSet;
struct RandomizedSet {
list: HashSet<i32>,
}
/**
* `&self` means the method takes an immutable reference.
* If you need a mutable reference, change it to `&mut self` instead.
*/
impl RandomizedSet {
fn new() -> Self {
Self {
list: HashSet::new(),
}
}
fn insert(&mut self, val: i32) -> bool {
self.list.insert(val)
}
fn remove(&mut self, val: i32) -> bool {
self.list.remove(&val)
}
fn get_random(&self) -> i32 {
let i = rand::thread_rng().gen_range(0, self.list.len());
*self.list.iter().collect::<Vec<&i32>>()[i]
}
}Complexity comparison
| Approach | Complexity |
|---|---|
| Divide and Conquer Approach | Time Complexity: O(n log n) |
| Iterative Approach using Heap Sort | Time Complexity: O(n log n) |
| Hash Table + Dynamic List | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Divide and Conquer (Array + Hash Map) | O(1) average for insert, delete, getRandom | O(n) | General case when constant-time operations are required |
| Iterative using Heap Sort | O(log n) insert/delete, O(1) random access | O(n) | When using heap-based structures or learning alternative dynamic storage approaches |
Frequently asked questions
How to solve Insert Delete GetRandom O(1)?
Store elements in a list (or dynamic array) and maintain a hash map that maps values to their indices. Insert pushes the value into the array and records its index. Remove swaps the element with the last array element, updates the map, and pops the array. getRandom returns the element at a randomly generated index.
Is Insert Delete GetRandom O(1) easy or hard?
Insert Delete GetRandom O(1) is classified as a Medium difficulty problem. The challenge is recognizing that a single data structure cannot support all operations efficiently, so combining a hash map with an array provides the required constant-time performance.
Insert Delete GetRandom O(1) Python/Java solution
Most Python and Java solutions implement a class that stores an ArrayList or list along with a HashMap or dictionary mapping values to indices. The swap-with-last trick ensures deletions stay O(1), and built-in random number generation returns a random element.
What is the best approach for Insert Delete GetRandom O(1)?
The optimal approach uses a combination of a dynamic array and a hash map. The array enables constant-time random access, while the hash map tracks each value's index for quick lookup and deletion. By swapping the element to delete with the last array element and updating the index in the map, all operations run in average O(1) time.
Is Insert Delete GetRandom O(1) asked at Google/Amazon/Meta?
Insert Delete GetRandom O(1) is a common interview question at large tech companies including Google, Amazon, Meta, and Microsoft. It evaluates your ability to design efficient data structures and combine arrays with hash maps for constant-time operations.
What data structure is used in Insert Delete GetRandom O(1)?
The standard solution uses two structures: a dynamic array for O(1) random access and a hash table that maps each value to its index in the array. This combination allows constant-time insertions, deletions, and random retrieval.
What is the time complexity of Insert Delete GetRandom O(1)?
The optimized data structure achieves average O(1) time for insert, remove, and getRandom operations. Insert appends to the array and records the index in a hash map. Remove swaps the element with the last item before deletion, and getRandom selects a random index from the array.
