一.题目描述
实现支持下列接口的「快照数组」- 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
的值。
二.题目分析
(1)上岸第一步:理清思路
实现一个快照数组,满足四个接口函数,通过题意分析可知,每次set操作后都会形成一个新的数组并覆盖原数组,即会多一个历史版本;而snap函数则是需要返回历史版本的个数(从0开始计数);get(int index,int snap_id)函数则要返回第snap_id个历史版本的第index个元素的值
因此,我们的历史版本数组必须保存下来,其保存方法有很多考究,是每次都赋值一个新的数组呢还是有其他新的方法呢?如果次次复制新的数组,无疑内存会爆掉的,因此我们需要一个简单且占用内存较小的方式来保留历史数据。
我采用的是开一个vector数组将元素序号、历史版本、元素值结合起来存储的方法,vector数组中每个元素也是一个vector数组,里面存储的是对应历史版本和对应元素数值(干说可能有点难以解,别担心,我们用例子模演一遍,友友们就可以更好地理解了,如下)
(2)上岸第二步:分模块实践代码
我们需要一个data数组来存储其历史数据和一个计数器来记录历史版本个数:
vector<vector<pair<int,int>>> data;//历史版本数据存储数组
int snap_cnt;//存储历史版本个数
.SnapshotArray():初始化元素全为0
SnapshotArray(int length):snap_cnt(0),data(length){}
//构造函数,初始化成员变量snap_cnt的值和data数组的长度
void set(index,val):将第index个元素设置为val,并开始记录历史版本数据
void set(int index,int val)
{
data[index].emplace_back(snap_cnt, val);
//emplace函数在向量末尾直接构造一个符合数据类型的元素,避免额外复制和移动操作
}
int snap():获取快照次数,即历史版本个数
int snap()
{
return snap_cnt;
}
int get(int index,int snap_id):返回第snap_id个历史版本的第index个元素的值
int get(int index,int snap_id)
{
auto x=upper_bound(data[index].begin(),data[index].end(),pair{snap_id+1,-1});
//得到第index个元素的所有取值中第一个大于{snap_id+1,-1}的元素的位置,并赋值给x
return x == data[index].begin() ? 0 : prev(x)->second;
//x是否为这个元素桶的起始数值,是则返回0,如果不是,则返回x得到的数组对的第二个值,即要返回的第index个元素的第snap_id版本的数值
}
(3)上岸第三步:整合思路,上完整代码
class SnapshotArray {
public:
SnapshotArray(int length): snap_cnt(0), data(length) {}
//初始化快照数组,其元素为0
void set(int index, int val) {
data[index].emplace_back(snap_cnt, val);
}//将指定索引的元素值设置为val
int snap() {
return snap_cnt++;
}
int get(int index, int snap_id) {
auto x = upper_bound(data[index].begin(), data[index].end(), pair{snap_id + 1, -1});
return x == data[index].begin() ? 0 : prev(x)->second;
}
private:
int snap_cnt;
vector<vector<pair<int, int>>> data;
};
(4)上岸最后一步:反思+总结
个人认为这道题还挺难的,最迷惑人的点其实是题目意思解读,题目理解对真的非常重要!!!理解了题目意思,思维逻辑和代码实现并不是很难,主要优化内存和运行速度用到了二分和vector数组的相关应用,也不难,直接调库就行,简而言之,友友们一定要理解对题目意思,一起噶油呀