实现支持下列接口的「快照数组」- SnapshotArray:
SnapshotArray(int length)
- 初始化一个与指定长度相等的 类数组 的数据结构。初始时,每个元素都等于 0。void set(index, val)
- 会将指定索引index
处的元素设置为val
。int snap()
- 获取该数组的快照,并返回快照的编号snap_id
(快照号是调用snap()
的总次数减去1
)。int get(index, snap_id)
- 根据指定的snap_id
选择快照,并返回该快照指定索引index
的值。
示例:
输入:["SnapshotArray","set","snap","set","get"] [[3],[0,5],[],[0,6],[0,0]] 输出:[null,null,0,null,5] 解释: SnapshotArray snapshotArr = new SnapshotArray(3); // 初始化一个长度为 3 的快照数组 snapshotArr.set(0,5); // 令 array[0] = 5 snapshotArr.snap(); // 获取快照,返回 snap_id = 0 snapshotArr.set(0,6); snapshotArr.get(0,0); // 获取 snap_id = 0 的快照中 array[0] 的值,返回 5
提示:
1 <= length <= 50000
- 题目最多进行
50000
次set
,snap
,和get
的调用 。 0 <= index < length
0 <= snap_id <
我们调用snap()
的总次数0 <= val <= 10^9
初始快照数组最长50000,最多50000次snap调用,也就是最多产生50000个长度为50000的快照,如果每次快照时都存储完整的数组,将超出内存限制。
观察set函数,每次只set当前快照中一个位置的数。
那么可以构造一个结构,其中每个位置保存当前下标变化的历史:
vector<vector<pair<int,int>>> history;
history中第 i 处下标表示快照数组中第 i 个数的变化历史。每次set(index,value)时,向history[index]中追加一条记录:pair{snap_now,val}。snap_now中保存了当前快照计数。
void set(int index, int val) {
history[index].emplace_back(snap_now,val);
}
获取快照就返回snap_now后将snap_now加1:
int snap() {
return snap_now++;
}
而对于get函数,考虑我们在set中更新history[index]的顺序:快照id是递增的,set也是随着快照id设置的,因此,history[index]中的数组是有序的(对于第一个元素即快照id)。
考虑使用二分查找,查找目标为快照数组中第 i 个数的变化历史中比snap_id大的第一个数:
int get(int index, int snap_id) {
auto upper = upper_bound(history[index].begin(),history[index].end(),pair{snap_id + 1,-1});
return upper == history[index].begin() ? 0 : prev(upper)->second;
}
例如:history[0]=[1,6),(2,1),(3,3),(3,4),(4,5)],get(0,3)想要返回的是(3,4)这对,那么只要找到(4,5)这里,即第一个比snap_id大的pair,然后取它的前置节点。
完整代码:
class SnapshotArray {
public:
SnapshotArray(int length) : history(length){
}
void set(int index, int val) {
history[index].emplace_back(snap_now,val);
}
int snap() {
return snap_now++;
}
int get(int index, int snap_id) {
auto upper = upper_bound(history[index].begin(),history[index].end(),pair{snap_id + 1,-1});
return upper == history[index].begin() ? 0 : prev(upper)->second;
}
private:
int snap_now = 0;
vector<vector<pair<int,int>>> history;
};
/**
* Your SnapshotArray object will be instantiated and called as such:
* SnapshotArray* obj = new SnapshotArray(length);
* obj->set(index,val);
* int param_2 = obj->snap();
* int param_3 = obj->get(index,snap_id);
*/
c++中提供上界(upper_bound)和下界(lower_bound)函数,内部实现是二分查找。
在 C++ 标准库中,upper_bound
和 lower_bound
函数用于在有序容器中查找元素的边界。
iterator upper_bound(iterator first, iterator last, const T& value);
iterator lower_bound(iterator first, iterator last, const T& value);
其中:
first
和last
是容器的开始和结束迭代器。value
是要查找的值。
如果容器中没有大于或等于给定值的元素(对于lower_bound)或没有大于给定值的元素(对于upper_bound),则返回last
。