Design a Data Structure that can support the following operations in O(1) Time Complexity.
- insert(x): Inserts x in the data structure. Returns True if x was not present and False if it was already present.
- remove(x): Removes x from the data structure, if present.
- getRandom(): Returns any value present in the stream randomly. The probability of each element being returned should be linearly proportional to the number of the same valued elements the stream contains.
Approach:
In the previous article, we have already discussed an approach for this kind of data structure. However, the previous data structure worked well only for unique values. In this article, we will design a data structure that can handle duplicate elements also. The approach used in this article is much similar to the previous approach, but in order to handle the duplicate elements, a map of sets is used to store the indices of the elements present in the dynamic array. Let’s understand every method independently.
-
insert(int x):
- Insert x at the end of the dynamic array nums[].
- Insert the index of x (i.e.) nums.size() – 1 to mp[x]. This map of sets stores all the indices of the element x present in the dynamic array nums[].
-
remove(int x):
- Check if x is present in the stream by mp.count(x). If it is absent, then return False.
- If x is present, the first element of the set mp[x] is deleted and its value is stored in a variable indexRemoved. Now, if this element (i.e.) indexRemoved is the same as nums.length() – 1 go directly to step 6 because this means that the element is already at the last index and that element is deleted in constant time.
- If not, then in order to delete this element in constant time, this element is swapped with the last element in the dynamic array. Therefore, delete the value nums.size() – 1 from the mp[nums[nums.size() – 1]] set.
- Insert the value index into mp[nums[nums.size() – 1]] set.
- Swap the elements at index nums.size() – 1 and indexRemoved of nums.
- Delete the last element from nums (Removal from the end from Dynamic Array is constant time operation).
- If the mp[val] set becomes empty, erase val from mp.
- Return True
-
getRandom():
- Get a random number between 0 and nums.size() – 1.
- Return the value present at this index of nums.
Below is the implementation of the above approach:
// C++ program to design a data structure // that supports insert, delete, // getRandom in O(1) with duplicates #include <bits/stdc++.h> using namespace std;
class Stream {
private :
// Stores all the numbers present
// currently in the stream
vector< int > nums;
// Unordered ensure O(1) operation
unordered_map< int , unordered_set< int > > mp;
public :
// Function to insert values
// in the stream
bool insert( int val)
{
// Inserting val to the end of array
nums.push_back(val);
// Index at which val was inserted
int index = nums.size() - 1;
// Inserting the index inside the
// set mp[val]
mp[val].insert(index);
// Return True if only one val
// is present in the stream
return mp[val].size() == 1;
}
// Function to remove the value
// from the stream
bool remove ( int val)
{
// If the value is not present
// in the stream
if (!mp.count(val))
return 0;
// Get the value of the first element
// of the mp[val] and store it
// in a variable named index
int index = *(mp[val].begin());
// Last Index of nums
int lastIndex = nums.size() - 1;
// Erase the index from mp[val] set
mp[val].erase(index);
// If index == lastIndex, then the
// element was already deleted
// from the stream
if (index != lastIndex) {
// Delete the lastIndex from
// mp[nums[lastIndex]] set
mp[nums[lastIndex]].erase(lastIndex);
// Insert index into mp[nums[lastIndex]] set
mp[nums[lastIndex]].insert(index);
// Swap the values at index and lastIndex
swap(nums[index], nums[lastIndex]);
}
// Delete the last element from nums
// This operation is O(1) operation
nums.pop_back();
// If the size of mp[val] is 0,
// val is absent from the stream
// and hence it is removed
if (mp[val].size() == 0)
mp.erase(val);
return 1;
}
// Function to get a random number
// from the stream of data
int getRandom()
{
// Get any random index from 0 to
// nums.length() - 1
int randomIndex = rand () % nums.size();
// Return the value at that index
return nums[randomIndex];
}
}; // Driver code int main()
{ Stream myStream;
cout << myStream.insert(5) << endl;
cout << myStream.insert(6) << endl;
cout << myStream.insert(5) << endl;
cout << myStream. remove (6) << endl;
cout << myStream. remove (6) << endl;
cout << myStream.getRandom() << endl;
return 0;
} |
// Java program to design a data structure // that supports insert, delete, // getRandom in O(1) with duplicates import java.util.*;
class Stream {
List<Integer> nums;
Map<Integer, Set<Integer> > mp;
public Stream()
{
nums = new ArrayList<>();
mp = new HashMap<>();
}
public boolean insert( int val)
{
nums.add(val);
int index = nums.size() - 1 ;
if (!mp.containsKey(val)) {
mp.put(val, new HashSet<>());
}
mp.get(val).add(index);
return mp.get(val).size() == 1 ;
}
public boolean remove( int val)
{
if (!mp.containsKey(val)) {
return false ;
}
Set<Integer> valIndices = mp.get(val);
int index = valIndices.iterator().next();
int lastIndex = nums.size() - 1 ;
valIndices.remove(index);
if (index != lastIndex) {
int lastNum = nums.get(lastIndex);
mp.get(lastNum).remove(lastIndex);
mp.get(lastNum).add(index);
Collections.swap(nums, index, lastIndex);
}
nums.remove(lastIndex);
if (valIndices.size() == 0 ) {
mp.remove(val);
}
return true ;
}
public int getRandom()
{
int randomIndex
= ( int )(Math.random() * nums.size());
return nums.get(randomIndex);
}
} public class Main {
public static void main(String[] args)
{
Stream myStream = new Stream();
System.out.println(myStream.insert( 5 ));
System.out.println(myStream.insert( 6 ));
System.out.println(myStream.insert( 5 ));
System.out.println(myStream.remove( 6 ));
System.out.println(myStream.remove( 6 ));
System.out.println(myStream.getRandom());
}
} |
''' Python program to design a data structure that supports insert, delete,
getRandom in O(1) with duplicates'''
import random
class Stream:
def __init__( self ):
# Stores all the numbers present
# currently in the stream
self .nums = []
# Unordered ensure O(1) operation
self .mp = {}
'''Function to insert values
in the stream'''
def insert( self , val):
# Inserting val to the end of array
self .nums.append(val)
# Index at which val was inserted
index = len ( self .nums) - 1
if val not in self .mp:
self .mp[val] = set ()
# Inserting the index inside the
# set mp[val]
self .mp[val].add(index)
# Return True if only one val
# is present in the stream
return len ( self .mp[val]) = = 1
# Function to remove the value
# from the stream
def remove( self , val):
# If the value is not present
# in the stream
if val not in self .mp:
return False
# Get the value of the first element
# of the mp[val] and store it
# in a variable named index
index = next ( iter ( self .mp[val]))
# Last Index of nums
last_index = len ( self .nums) - 1
# Erase the index from mp[val] set
self .mp[val].remove(index)
# If index == lastIndex, then the
# element was already deleted
# from the stream
if index ! = last_index:
last_num = self .nums[last_index]
# Delete the lastIndex from
# mp[nums[lastIndex]] set
self .mp[last_num].remove(last_index)
# Insert index into mp[nums[lastIndex]] set
self .mp[last_num].add(index)
# Swap the values at index and lastIndex
self .nums[index], self .nums[last_index] = self .nums[last_index], self .nums[index]
# Delete the last element from nums
# This operation is O(1) operation
self .nums.pop()
# If the size of mp[val] is 0,
# val is absent from the stream
# and hence it is removed
if not self .mp[val]:
del self .mp[val]
return True
# Function to get a random number
# from the stream of data
def getRandom( self ):
# Get any random index from 0 to
# nums.length() - 1
random_index = random.randint( 0 , len ( self .nums) - 1 )
# Return the value at that index
return self .nums[random_index]
# Driver code myStream = Stream()
print (myStream.insert( 5 ))
print (myStream.insert( 6 ))
print (myStream.insert( 5 ))
print (myStream.remove( 6 ))
print (myStream.remove( 6 ))
print (myStream.getRandom())
# This code is contributed by shivhack999 |
using System;
using System.Collections.Generic;
using System.Linq;
// Class representing a stream with O(1) insert, delete, and getRandom operations class Stream
{ private List< int > nums; // Stores all the numbers present currently in the stream
private Dictionary< int , HashSet< int >> mp; // Dictionary to ensure O(1) operation
// Constructor initializes the stream
public Stream()
{
nums = new List< int >();
mp = new Dictionary< int , HashSet< int >>();
}
// Function to insert values in the stream
public bool Insert( int val)
{
nums.Add(val);
int index = nums.Count - 1;
if (!mp.ContainsKey(val))
mp[val] = new HashSet< int >();
mp[val].Add(index);
return mp[val].Count == 1;
}
// Function to remove the value from the stream
public bool Remove( int val)
{
if (!mp.ContainsKey(val))
return false ;
int index = mp[val].First();
int lastIndex = nums.Count - 1;
mp[val].Remove(index);
if (index != lastIndex)
{
mp[nums[lastIndex]].Remove(lastIndex);
mp[nums[lastIndex]].Add(index);
nums[index] = nums[lastIndex];
}
nums.RemoveAt(lastIndex);
if (mp[val].Count == 0)
mp.Remove(val);
return true ;
}
// Function to get a random number from the stream of data
public int GetRandom()
{
int randomIndex = new Random().Next(0, nums.Count);
return nums[randomIndex];
}
} // Driver code class Program
{ static void Main()
{
Stream myStream = new Stream();
Console.WriteLine(myStream.Insert(5));
Console.WriteLine(myStream.Insert(6));
Console.WriteLine(myStream.Insert(5));
Console.WriteLine(myStream.Remove(6));
Console.WriteLine(myStream.Remove(6));
Console.WriteLine(myStream.GetRandom());
}
} |
class Stream { constructor() {
// Stores all the numbers present currently in the stream
this .nums = [];
// Map to store indices of each value in the stream
this .mp = new Map();
}
// Function to insert values in the stream
insert(val) {
// Inserting val to the end of array
this .nums.push(val);
// Index at which val was inserted
const index = this .nums.length - 1;
// Inserting the index inside the set mp[val]
if (! this .mp.has(val)) {
this .mp.set(val, new Set());
}
this .mp.get(val).add(index);
// Return 1 if only one val is present in the stream
return this .mp.get(val).size === 1 ? 1 : 0;
}
// Function to remove the value from the stream
remove(val) {
// If the value is not present in the stream
if (! this .mp.has(val)) {
return 0;
}
// Get the value of the first element of the mp[val] and store it in a variable named index
const index = this .mp.get(val).values().next().value;
// Last Index of nums
const lastIndex = this .nums.length - 1;
// Erase the index from mp[val] set
this .mp.get(val). delete (index);
// If index == lastIndex, then the element was already deleted from the stream
if (index !== lastIndex) {
// Delete the lastIndex from mp[nums[lastIndex]] set
this .mp.get( this .nums[lastIndex]). delete (lastIndex);
// Insert index into mp[nums[lastIndex]] set
this .mp.get( this .nums[lastIndex]).add(index);
// Swap the values at index and lastIndex
[ this .nums[index], this .nums[lastIndex]] = [ this .nums[lastIndex], this .nums[index]];
}
// Delete the last element from nums
// This operation is O(1) operation
this .nums.pop();
// If the size of mp[val] is 0, val is absent from the stream and hence it is removed
if ( this .mp.get(val).size === 0) {
this .mp. delete (val);
}
return 1;
}
// Function to get a random number from the stream of data
getRandom() {
// Get any random index from 0 to nums.length - 1
const randomIndex = Math.floor(Math.random() * this .nums.length);
// Return the value at that index
return this .nums[randomIndex];
}
} // Driver code const myStream = new Stream();
console.log(myStream.insert(5)); console.log(myStream.insert(6)); console.log(myStream.insert(5)); console.log(myStream.remove(6)); console.log(myStream.remove(6)); console.log(myStream.getRandom()); |
1 1 0 1 0 5