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 * 10^4
0 <= index < length
0 <= val <= 10^9
0 <= snap_id < (the total number of times we call snap())
At most 5 * 10^4 calls will be made to set, snap, and get.
Use a HashMap to keep track of every change. Use snap_id
as the key, use a list as the value. The list is the current array. Every time the snap
function is called, duplicate the previous array to form a new array.
Time complexity: Linear with the time of calling get
function
Space complexity: In the worst scenario, it would use 5 ∗ 1 0 4 ∗ 5 ∗ 1 0 4 = 25 ∗ 1 0 8 5*10^4*5*10^4=25*10^8 5∗104∗5∗104=25∗108 space.
In the previous solution, we would encounter memory limit error. It was because we keep track of every value in every change, even when most of them are the same. So this time, we need to reduce the space complexity at the cost of time complexity.
Use a list of list to keep track of every index, and only append values when the value at the index was changed. When get
function was called, use binary search to find the largest snap_id that is smaller than the snap_id in get
function.
class SnapshotArray:
def __init__(self, length: int):
self.value = {0: [0] * length}
self.snap_id = 0
def set(self, index: int, val: int) -> None:
self.value[self.snap_id][index] = val
def snap(self) -> int:
self.value[self.snap_id + 1] = self.value[self.snap_id].copy()
self.snap_id += 1
return self.snap_id - 1
def get(self, index: int, snap_id: int) -> int:
return self.value[snap_id][index]
# Your SnapshotArray object will be instantiated and called as such:
# obj = SnapshotArray(length)
# obj.set(index,val)
# param_2 = obj.snap()
# param_3 = obj.get(index,snap_id)
class SnapshotArray:
def __init__(self, length: int):
self.snap_id = 0
self.value = [[(self.snap_id, 0)] for _ in range(length)]
def set(self, index: int, val: int) -> None:
self.value[index].append((self.snap_id, val))
def snap(self) -> int:
self.snap_id += 1
return self.snap_id - 1
def get(self, index: int, snap_id: int) -> int:
def find_largest_snap_id(id_value: list, snap_id: int) -> int:
left, right = 0, len(id_value) - 1
while left < right:
mid = (left + right + 1) // 2
if id_value[mid][0] > snap_id:
right = mid - 1
else:
left = mid
return (left + right + 1) // 2
# use bisect to find the largest store_snap_id that is smaller than snap_id
res_index = find_largest_snap_id(self.value[index], snap_id)
return self.value[index][res_index][1]
# Your SnapshotArray object will be instantiated and called as such:
# obj = SnapshotArray(length)
# obj.set(index,val)
# param_2 = obj.snap()
# param_3 = obj.get(index,snap_id)