Interview Question:
You are given an array of size N to implement a set data structure. The set should support the following operations:
x into the set, where 0 ≤ x < N.x from the set.x is present in the set.All operations should have a time complexity of O(1).
Constraints:
[0, N-1].N.To achieve all operations in O(1) time, we can implement a data structure known as a sparse set. This structure leverages arrays and direct indexing to provide constant-time insertions, deletions, lookups, and clear operations.
values array.Clear() to reset the set efficiently.N).package main
import "fmt"
type FastSet struct {
size int
values []int
indices []int
timestamps []int
timeStamp int
capacity int
}
// NewFastSet initializes a new FastSet with capacity N
func NewFastSet(N int) *FastSet {
return &FastSet{
values: make([]int, N),
indices: make([]int, N),
timestamps: make([]int, N),
timeStamp: 1,
capacity: N,
}
}
// Insert adds x to the set if it's not already present
func (fs *FastSet) Insert(x int) {
if x < 0 || x >= fs.capacity {
fmt.Printf("Value %d out of bounds\n", x)
return
}
if fs.timestamps[x] != fs.timeStamp {
fs.timestamps[x] = fs.timeStamp
fs.indices[x] = fs.size
fs.values[fs.size] = x
fs.size++
}
}
// Remove deletes x from the set if it's present
func (fs *FastSet) Remove(x int) {
if x < 0 || x >= fs.capacity {
fmt.Printf("Value %d out of bounds\n", x)
return
}
if fs.timestamps[x] == fs.timeStamp {
idx := fs.indices[x]
last := fs.values[fs.size-1]
fs.values[idx] = last
fs.indices[last] = idx
fs.size--
fs.timestamps[x] = 0 // Optional: reset timestamp
}
}
// Contains checks if x is in the set
func (fs *FastSet) Contains(x int) bool {
if x < 0 || x >= fs.capacity {
fmt.Printf("Value %d out of bounds\n", x)
return false
}
return fs.timestamps[x] == fs.timeStamp
}
// Clear empties the set
func (fs *FastSet) Clear() {
fs.size = 0
fs.timeStamp++
if fs.timeStamp == 0 { // Handle overflow
fs.timeStamp++
}
}
// Iterate over the elements in the set
func (fs *FastSet) Iterate() []int {
return fs.values[:fs.size]
}
func main() {
N := 10
set := NewFastSet(N)
// Insert elements
set.Insert(3)
set.Insert(5)
set.Insert(7)
fmt.Println("Set after inserts:", set.Iterate())
// Check if elements are in the set
fmt.Println("Contains 5?", set.Contains(5)) // true
fmt.Println("Contains 2?", set.Contains(2)) // false
// Remove an element
set.Remove(5)
fmt.Println("Set after removing 5:", set.Iterate())
fmt.Println("Contains 5?", set.Contains(5)) // false
// Insert a new element
set.Insert(2)
fmt.Println("Set after inserting 2:", set.Iterate())
fmt.Println("Contains 2?", set.Contains(2)) // true
// Clear the set
set.Clear()
fmt.Println("Set after clearing:", set.Iterate())
fmt.Println("Contains 3?", set.Contains(3)) // false
// Insert after clearing
set.Insert(1)
set.Insert(9)
fmt.Println("Set after inserting 1 and 9:", set.Iterate())
fmt.Println("Contains 1?", set.Contains(1)) // true
fmt.Println("Contains 9?", set.Contains(9)) // true
}x is within bounds (0 ≤ x < N).timestamps[x] to check if x is already in the set.
timestamps[x] to the current timeStamp.x to values[size].indices[x] with the current size.size.x is within bounds.timestamps[x] to check if x is in the set.
idx of x in values.values[idx] with the last element values[size-1].indices for the swapped element.size.timestamps[x] (not necessary due to timeStamp logic).x is within bounds.true if timestamps[x] == timeStamp, indicating that x is in the set.false.size to 0.timeStamp to invalidate all previous timestamps.timeStamp.values up to size, which contains all current elements in the set.Set after inserts: [3 5 7]
Contains 5? true
Contains 2? false
Set after removing 5: [3 7]
Contains 5? false
Set after inserting 2: [3 7 2]
Contains 2? true
Set after clearing: []
Contains 3? false
Set after inserting 1 and 9: [1 9]
Contains 1? true
Contains 9? trueA sparse set allows us to represent a set of integers from a fixed range efficiently. By using arrays and direct indexing, we can achieve constant-time operations.
values[] if it's in the set.timeStamp.timeStamp starts at 1.x is considered in the set if timestamps[x] == timeStamp.timeStamp effectively clears the set, as all previous timestamps become invalid.timeStamp wraps around to 0, we handle it by ensuring it never equals 0.Contains(x int) bool function allows us to check if an element x is present in the set in O(1) time.timestamps[x] equals the current timeStamp.Resetting the entire timestamps array on a Clear() operation would take O(N) time. By incrementing timeStamp, we avoid this overhead, keeping the Clear() operation at O(1).
When removing an element, we swap it with the last element in values[] to maintain a contiguous block of active elements. This allows for efficient iteration over the set.