Implement a SnapshotArray that supports the following interface:
SnapshotArray(int length)
initializes an array-like data structure with the given length. Initially, each element equals 0.void set(index, val)
sets the element at the given index
to be equal to val
.int snap()
takes a snapshot of the array and returns the snap_id
: the total number of times we called snap()
minus 1
.int get(index, snap_id)
returns the value at the given index
, at the time we took the snapshot with the given snap_id
Example 1:
Input: ["SnapshotArray","set","snap","set","get"] [[3],[0,5],[],[0,6],[0,0]] Output: [null,null,0,null,5] Explanation: SnapshotArray snapshotArr = new SnapshotArray(3); // set the length to be 3 snapshotArr.set(0,5); // Set array[0] = 5 snapshotArr.snap(); // Take a snapshot, return snap_id = 0 snapshotArr.set(0,6); snapshotArr.get(0,0); // Get the value of array[0] with snap_id = 0, return 5
Constraints:
1 <= length <= 5 * 104
0 <= index < length
0 <= val <= 109
0 <= snap_id <
(the total number of times we call snap()
)5 * 104
calls will be made to set
, snap
, and get
.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The snapshot array problem requires us to track the history of an array's values. A brute force method involves making a complete copy of the array every time we take a snapshot, storing each version separately.
Here's how the algorithm would work step-by-step:
class SnapshotArray:
def __init__(self, array_length):
self.array = [0] * array_length
self.snapshots = {0: self.array[:]}
self.current_snapshot_id = 0
def set(self, index, value):
self.array[index] = value
def snap(self):
# Save a copy of the current array state
self.current_snapshot_id += 1
self.snapshots[self.current_snapshot_id] = self.array[:]
return self.current_snapshot_id - 1
def get(self, index, snapshot_id):
# Find the correct snapshot to retrieve from
while snapshot_id not in self.snapshots and snapshot_id > 0:
snapshot_id -= 1
# If snapshot doesn't exist, return 0.
if snapshot_id not in self.snapshots:
return 0
# Access value from the correct snapshot
return self.snapshots[snapshot_id][index]
The key idea is to avoid storing the entire array at each snapshot. Instead, we only store what's changed since the last snapshot, saving a lot of space and time. Think of it like version control for an array.
Here's how the algorithm would work step-by-step:
class SnapshotArray:
def __init__(self, array_length):
self.array_length = array_length
self.history = [{} for _ in range(array_length)]
self.current_snapshot_id = 0
def set(self, index, value):
# Store changes with the current snapshot ID
self.history[index][self.current_snapshot_id] = value
def snap(self):
self.current_snapshot_id += 1
return self.current_snapshot_id - 1
def get(self, index, snapshot_id):
# Find the most recent value set before or during snapshot_id
for snap_id in reversed(sorted(self.history[index].keys())):
if snap_id <= snapshot_id:
#Return the value if snapshot id is before target
return self.history[index][snap_id]
# If no value was set before snapshot_id, return 0.
return 0
Case | How to Handle |
---|---|
Initializing SnapshotArray with size 0 | The array is initialized with size 0, so all set operations become no-ops and get operations should return 0. |
Calling snap() many times consecutively | The snap_id counter should increment correctly and not overflow within reasonable limits, allocating new snapshot IDs as expected. |
Setting a value multiple times at the same index before a snap() | Only the last set value should be considered when a snap() is called. |
Getting a value from a snapshot that doesn't exist | Return 0 if the snapshot id does not exist, as this is the initial default value. |
Setting negative values in the array | The set function should handle negative values correctly, storing them without any issues. |
Setting and getting values close to the integer limits | Ensure that operations don't cause integer overflows during set/get operations. |
Calling set with an index out of bounds | The set operation should either throw an exception or be ignored, or update array size if it increases the bound (less preferred). |
Memory usage with a very large number of snapshots | Consider using a more space-efficient data structure for storing snapshots if memory becomes a bottleneck, such as a persistent data structure or only storing differences (deltas) between snapshots. |