leetcode-快照数组

193 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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);
 */