【每日一题】981. 基于时间的键值存储
981. 基于时间的键值存储 - 力扣(LeetCode)
设计一个基于时间的键值数据结构,该结构可以在不同时间戳存储对应同一个键的多个值,并针对特定时间戳检索键对应的值。
实现
TimeMap
类:
TimeMap()
初始化数据结构对象void set(String key, String value, int timestamp)
存储键key
、值value
,以及给定的时间戳timestamp
。String get(String key, int timestamp)
- 返回先前调用
set(key, value, timestamp_prev)
所存储的值,其中timestamp_prev <= timestamp
。- 如果有多个这样的值,则返回对应最大的
timestamp_prev
的那个值。- 如果没有值,则返回空字符串(
""
)。示例:
输入: ["TimeMap", "set", "get", "get", "set", "get", "get"] [[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]] 输出: [null, null, "bar", "bar", null, "bar2", "bar2"]解释: TimeMap timeMap = new TimeMap(); timeMap.set("foo", "bar", 1); // 存储键 "foo" 和值 "bar" ,时间戳 timestamp = 1 timeMap.get("foo", 1); // 返回 "bar" timeMap.get("foo", 3); // 返回 "bar", 因为在时间戳 3 和时间戳 2 处没有对应 "foo" 的值,所以唯一的值位于时间戳 1 处(即 "bar") 。 timeMap.set("foo", "bar2", 4); // 存储键 "foo" 和值 "bar2" ,时间戳 timestamp = 4 timeMap.get("foo", 4); // 返回 "bar2" timeMap.get("foo", 5); // 返回 "bar2"提示:
1 <= key.length, value.length <= 100
key
和value
由小写英文字母和数字组成1 <= timestamp <= 107
set
操作中的时间戳timestamp
都是严格递增的- 最多调用
set
和get
操作2 * 105
次
class TimeMap {//假设key一样//HashMap<Integer , String> map = new HashMap<Integer , String>();HashMap<String,HashMap<Integer , String>> map = new HashMap<String,HashMap<Integer , String>>();// ArrayList<Integer> arr = new ArrayList<Integer>();HashMap<String,ArrayList<Integer>> arr = new HashMap<String,ArrayList<Integer>>();public TimeMap() {arr.clear();map.clear();}public void set(String key, String value, int timestamp) {//arr.add(timestamp);//map.put(timestamp,value);if(!map.containsKey(key)) {HashMap<Integer , String> tmp = new HashMap<Integer , String>();tmp.put(timestamp,value);map.put(key,tmp);} else {map.get(key).put(timestamp,value);}if(!arr.containsKey(key)) {ArrayList<Integer> tmp = new ArrayList<Integer>();tmp.add(timestamp);arr.put(key,tmp);} else {arr.get(key).add(timestamp);}}public String get(String key, int timestamp) {if(!map.containsKey(key)) return "";ArrayList<Integer> arr1 = arr.get(key);int right = arr1.size()-1;int left = 0;int mid = 0;while(left <= right) {mid = (left+right)/2;if(arr1.get(mid) < timestamp) {left = mid+1;} else if(arr1.get(mid) > timestamp) {right = mid-1;} else {return map.get(key).get(arr1.get(mid));}}if(left == 0) return "";return map.get(key).get(arr1.get(left-1));// int right = arr.size()-1;// int left = 0;// int mid = 0;// while(left <= right) {// mid = (left+right)/2;// if(arr.get(mid) < timestamp) {// left = mid+1;// } else if(arr.get(mid) > timestamp) {// right = mid-1;// } else {// System.out.println(mid);// System.out.println("------------");// return map.get(arr.get(mid));// }// }// if(left == 0) return "";// return map.get(arr.get(left-1));}
}/*** Your TimeMap object will be instantiated and called as such:* TimeMap obj = new TimeMap();* obj.set(key,value,timestamp);* String param_2 = obj.get(key,timestamp);*/
每日一题,今天是中等题。这道题有些难度,但主要也是在理解题目上。
首先读题,了解到,实际上是一个键值对。在java中,键值对一般使用map来存储。但是,除了key和value,还有一个timestamp。所以实际上是需要一个中介的。题目的set函数实际上就是存储键值对的过程。get函数才是获得答案的过程。由于key会不一样,如果一开始就上手可能会很复杂,我们可以从简单的写起,一步一步往上。
先假设只有一个key的情况。那么key是不用存储的。get要找到给定timestamp前的最大数,那实际上就是要找<=timestamp的最大数。最后肯定是要根据找到的这个timestamp来返回值的,所以,一开始的键值对我们可以用(Integer,String)。之后用一个arraylist来存储timestamp。题目的数量级是2*105次方,如果直接暴力查找,大概率过不了。所以需要使用二分,改一下条件,让其变成可以找到小于等于timestamp的最大数。按博主的写法,最后left会是小于等于timestamp最大数的前一个。当然,博主的写法如果找到了就会直接返回了。之后只要根据找到的timestamp去map里取值就可以了。
一个key的情况解决了,就是多个key的问题了。多个key无非就是用hashmap把一种key的情况包起来。所以一开始存储的两个数据结构都要用hashmap包起来,一个key对应一个map<Integer,String>和一个arraylist。之后就是修改set函数了:如果原本不存在这个key,就要新建一个新的map<Integer,String>和一个新的arraylist来存储这个key对应的value和timestamp。如果存在直接获取添加即可。再往后就是修改get函数了:如果不存在这个key对应的map或arraylist就说明还没有添加进来过,直接返回空字符串就行;如果存在,就按刚才一个的方法去做就可以了。
今天这道题,用到了二分的变式,还是很有意义的,而且能够锻炼大家数据结构使用的熟练度。不过时间复杂度和空间复杂度都很大,大家可以去看看题解寻找更快更好的解决方法。