持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
题目描述
实现支持下列接口的「快照数组」- 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
思路
数据结构的题目,思路也比较直接。最容易想到的是,直接模拟,每次在snap方法被调用的时候存下一个快照的版本,但是这样对空间很不友好,如果数据没有任何改变,但是一直在调用snap方法,占用的空间就会一直增加。
我们的方法是:对于数组中的每一个元素,用一个List存下这个元素的所有历史的值,这样,整体的结构就变成了List。这样还不够,里面的List还不能直接存值,这样就不知道版本号,我们定义一个内部类Item,存下值和版本号2个属性。这样,我们需要get的时候,对于每个元素,我们用二分法在内层List去查找满足小于等于版本号的最大版本号的值来返回。
Java版本代码
class SnapshotArray {
List<List<Item>> list;
int version;
public SnapshotArray(int length) {
version = 0;
list = new ArrayList<>();
for (int i = 0; i < length; i++) {
List<Item> l = new ArrayList<>();
l.add(new Item(version, 0));
list.add(l);
}
}
public void set(int index, int val) {
List<Item> l = list.get(index);
Item last = l.get(l.size()-1);
if (last.version == version) {
last.val = val;
} else {
l.add(new Item(version, val));
}
}
public int snap() {
return version++;
}
public int get(int index, int snap_id) {
List<Item> l = list.get(index);
int left = 0, right = l.size()-1;
int targetIndex = -1;
while (left <= right) {
int mid = left + ((right - left)>>1);
Item item = l.get(mid);
if (item.version <= snap_id) {
targetIndex = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return l.get(targetIndex).val;
}
class Item {
int version;
int val;
public Item(int version, int val) {
this.version = version;
this.val = val;
}
}
}
/**
* 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);
*/