leetcode1146. 快照数组

在这里插入图片描述

LeetCode系列文章

文章目录

  • 一、题目描述
  • 二、示例
  • 三、主体思路
    • 1、模拟
    • 2、哈希+二分法
  • 四、代码实现
    • 1、模拟
    • 2、哈希+二分法

一、题目描述

实现支持下列接口的「快照数组」- S n a p s h o t A r r a y SnapshotArray SnapshotArray

  • S n a p s h o t A r r a y ( i n t   l e n g t h ) SnapshotArray(int\space length) SnapshotArray(int length) - 初始化一个与指定长度相等的类数组的数据结构。初始时,每个元素都等于0。
  • v o i d   s e t ( i n d e x ,   v a l ) void\space set(index,\space val) void set(index, val) - 会将指定索引 i n d e x index index 处的元素设置为 v a l val val
  • i n t   s n a p ( ) int\space snap() int snap() - 获取该数组的快照,并返回快照的编号 s n a p _ i d snap\_id snap_id(快照号是调用 s n a p ( ) snap() snap() 的总次数减去1)。
  • i n t   g e t ( i n d e x ,   s n a p _ i d ) int\space get(index,\space snap\_id) int get(index, snap_id) - 根据指定的 s n a p _ i d snap\_id snap_id 选择快照,并返回该快照指定索引 i n d e x index index 的值。

二、示例

  输入: [ “ S n a p s h o t A r r a y " , “ s e t " , “ s n a p " , “ s e t " , “ g e t " ] [“SnapshotArray", “set", “snap", “set", “get"] [SnapshotArray",set",snap",set",get"]
      [ [ 3 ] , [ 0 , 5 ] , [ ] , [ 0 , 6 ] , [ 0 , 0 ] ] [ [3], [0, 5], [ ], [0, 6], [0, 0] ] [[3],[0,5],[],[0,6],[0,0]]
  输出: [ n u l l , n u l l , 0 , n u l l , 5 ] [null, null, 0, null, 5] [null,null,0,null,5]

  解释:
      S n a p s h o t A r r a y ∗   o b j = n e w   S n a p s h o t A r r a y ( 3 ) ;    / / 初 始 化 一 个 长 度 为 3 的 快 照 数 组 SnapshotArray*\space obj = new\space SnapshotArray(3);\space \space//初始化一个长度为3的快照数组 SnapshotArray obj=new SnapshotArray(3);  //3
      o b j − > s e t ( 0 , 5 ) ;    / / 令 a r r a y [ 0 ] = 5 obj->set(0, 5);\space \space//令array[0] = 5 obj>set(0,5);  //array[0]=5
      o b j − > s n a p ( ) ;    / / 获 取 快 照 , 返 回 s n a p i d = 0 obj->snap();\space \space//获取快照,返回snap_id = 0 obj>snap();  //snapid=0
      o b j − > s e t ( 0 , 6 ) ; obj->set(0, 6); obj>set(0,6);
      o b j − > g e t ( 0 , 0 ) ;    / / 获 取 s n a p _ i d = 0 的 快 照 中 a r r a y [ 0 ] 的 值 , 返 回 5 obj->get(0, 0);\space \space//获取snap\_id = 0的快照中array[0]的值,返回5 obj>get(0,0);  //snap_id=0array[0]5

三、主体思路

1、模拟

首先,模拟快照数组是最容易想到的一种方法。

  • 可以将一个普通数组看作快照数组,调用 s e t set set 函数设置快照数组当中的元素值,就是设置该普通数组当中的元素值。
  • 而当调用 s n a p snap snap 函数获取数组的快照时,需要将当前快照数组中各个元素的值进行备份,因为后续可能会调用 g e t get get 函数获取该快照当中的某一元素值。
  • 当调用 g e t get get 函数获取指定快照当中的某一元素值时,就可以根据快照号找到对应的快照数组,然后将对应的元素值进行返回即可。

  在C++中,可以用vector来充当这个普通数组,该普通数组当中存储的元素类型是int。而由于调用 s n a p snap snap 函数获取到的快照号,是从0开始向后依次递增,因此也可以用一个vector来存储每一个快照号对应的快照数组备份,该vector的下标代表的是某一快照号,而vector容器当中存储的元素则是一个个的普通数组,即存储元素的类型是vector

  但实际我们并不推荐模拟快照数组这个方法,因为每次调用 s n a p snap snap 函数都会将当前快照数组当中的每一个元素进行备份,此时就会导致大量不必要的备份。

2、哈希+二分法

快照数组可以根据快照号获取到曾经已经被修改过的元素值,这也就意味着:

  • 在调用 s e t set set 函数设置元素值时,不应该只是简单的设置元素值,而应该通过某种方式将当前的时间点也设置进去,也就是将设置的元素值在某种意义上与时间进行“绑定”。
  • 并且在设置元素值时,不应该将之前设置的元素值进行覆盖,这样我们才能找到之前设置过的元素值。
  • 这样一来,在调用 g e t get get 函数获取元素值时就可以获取在之前任意时间点设置过的元素值。

一、将元素值与时间进行“绑定”

  此时我们要做的就是在调用 s e t set set 函数设置元素值时,需要将该元素值与当前时间“绑定”后再进行设置。而要与时间“绑定”其实很简单,我们可以将调用 s e t set set 函数的次数 t i m e time time 进行记录,当需要设置元素值时设置的就不是一个简单的 v a l val val,而是一个 < t i m e , v a l > <time,val> 键值对。

  而由于我们不能将之前设置的元素值进行覆盖,因此我们可以将快照数组当中的每一个位置看作一个哈希桶,当我们要设置快照数组当中的某一元素值时,就将对应的 < t i m e , v a l > <time,val> 键值对挂到对应的哈希桶下面即可。

例如,按照所给示例设置快照数组后,快照数组的布局如下:
leetcode1146. 快照数组_第1张图片
  由于初始时快照数组当中每个元素的值都要初始化为0,因此 t i m e time time 为0快照数组中每个元素的值都是0,而由于后续调用了两次 s e t set set 函数将快照数组中第0个元素先后设置为了5和6,因此在第0号桶下还挂上了 < 1 , 5 > <1, 5> <1,5> < 2 , 6 > <2, 6> <2,6>键值对。

二、将快照号与时间进行“绑定”

  我们最终是要通过快照号来获取指定索引的元素,而现在我们只是将元素值与 t i m e time time 进行了“绑定”,因此接下来还需要将快照号与 t i m e time time 进行“绑定”。

  由于调用 s n a p snap snap 函数获取到的快照号是从0开始向后依次递增的,因此我们可以通过数组的方式建立快照号与 t i m e time time 的映射关系,其中快照号就作为数组的下标,而快照号对应的 t i m e time time 就作为该下标对应的元素值。

  因此当调用 s n a p snap snap 函数时,我们需要将当前的 t i m e time time 值存储到一个数组当中。比如第一次调用 s n a p snap snap 函数时,快照号为0, t i m e time time 的值为3;第二次调用 s n a p snap snap 函数时,快照号为1, t i m e time time 的值为5;第三次调用 s n a p snap snap 函数时,快照号为2, t i m e time time 的值为8,那么此时该数组的布局如下:
在这里插入图片描述

三、获取指定快照号对应索引的元素

  现在将元素值和快照号都与 t i m e time time 进行了“绑定”,那当我们要获取指定快照号对应索引 i n d e x index index 的元素时,是不是就是去快照数组中第 i n d e x index index 号哈希桶下面找到对应 t i m e time time 值的元素值就行了呢?

  理论上是这样的,例如在下面这个快照数组当中我们要获取快照号为0时第0个元素的值,此时就应该在第0个哈希桶下找到 t i m e time time 为2的键值对对应的元素值,也就是6。
leetcode1146. 快照数组_第2张图片
  但如果我们要获取快照号为0时第1个元素的值呢?此时在第1号哈希桶下是找不到 t i m e time time 为2的键值对的,在第1号哈希桶下没有 t i m e time time 为1和2对应的键值对,也就意味着在 t i m e time time 为1和2时快照数组中第1个元素的值没有发生变化,此时我们就应该将 t i m e time time 值为0的元素值进行返回。

  因此当我们要获取指定快照号对应索引 i n d e x index index 的元素时,应该在快照数组中第 i n d e x index index 号哈希桶下面找到第一个不大于对应 t i m e time time 值的元素值进行返回。

动图演示:

leetcode1146. 快照数组_第3张图片

四、代码实现

1、模拟

代码如下:
leetcode1146. 快照数组_第4张图片

2、哈希+二分法

代码如下:
leetcode1146. 快照数组_第5张图片
  上述代码中将 < t i m e , v a l > <time,val> 键值对类型进行了定义,并对该类型的 < < < 运算符进行了重载,因为代码中调用了 l o w e r _ b o u n d lower\_bound lower_bound 函数,该函数会用 < < < 符号对数据进行比较。

  实际这里我们并不是一定要进行键值对类型的定义,由于这里的每个 < t i m e , v a l > <time,val> 键值对当中存储的 t i m e time time 都一定是不同的,因此可以直接使用 p a i r pair pair 类型,因为 p a i r pair pair 类型的变量在比较时就是默认先比较第一个成员的大小,此时也能达到比较键值对中的 t i m e time time 的目的。
leetcode1146. 快照数组_第6张图片
说明一下:

  • 代码中的 l o w e r _ b o u n d lower\_bound lower_bound 函数只能找出不小于 t i m e time time 的键值对,而我们是要找出不大于 t i m e time time 的键值对,因此我们需要先找到不小于 t i m e + 1 time+1 time+1 的键值对,此时该键值对的前一个键值对就是不大于 t i m e time time 的键值对。

你可能感兴趣的:(leetcode,leetcode,算法,c++,哈希算法)