题目描述
题干:
创建一个基于时间的键值存储类 TimeMap,它支持下面两个操作:
1. set(string key, string value, int timestamp)
存储键 key、值 value,以及给定的时间戳 timestamp。
2. get(string key, int timestamp)
返回先前调用 set(key, value, timestamp_prev) 所存储的值
其中 timestamp_prev <= timestamp。
如果有多个这样的值,则返回对应最大的 timestamp_prev 的那个值。
如果没有值,则返回空字符串("")。
示例1:
输入:inputs = ["TimeMap","set","get","get","set","get","get"],
inputs = [[],["foo","bar",1],["foo",1],["foo",3],
["foo","bar2",4],["foo",4],["foo",5]]
输出:[null,null,"bar","bar",null,"bar2","bar2"]
解释:
TimeMap kv;
kv.set("foo", "bar", 1); // 存储键 "foo" 和值 "bar" 以及时间戳 timestamp = 1
kv.get("foo", 1); // 输出 "bar"
kv.get("foo", 3);
// 输出 "bar" 因为在时间戳 3 和时间戳 2 处没有对应 "foo" 的值
//所以唯一的值位于时间戳 1 处(即 "bar")
kv.set("foo", "bar2", 4);
kv.get("foo", 4); // 输出 "bar2"
kv.get("foo", 5); // 输出 "bar2"
示例2:
输入:inputs = ["TimeMap","set","set","get","get","get","get","get"]
inputs = [[],["love","high",10],["love","low",20],["love",5],
["love",10],["love",15],["love",20],["love",25]]
输出:[null,null,null,"","high","high","low","low"]
题解思路
设计TimeMap类,因为有三个变量需要考虑,所以我们把时间戳和value先分为一组,再和key分为一组
因为时间戳和value都需要进行判断,这里我们采用Pair(匹配)来存储这对键值对,将value作为key
之后用HashMap来存储key和Pair,这就完成了结构的设计,自己重写时间戳比较方法即可
正确代码
class TimeMap {
/**
* 自定义匹配方法存储value和时间戳
* 继承Comparable接口实现时间戳比较
*/
class Pair implements Comparable<Pair> {
// 声明value和时间戳
int timestamp;
String value;
// 构造函数
public Pair(int timestamp, String value) {
this.timestamp = timestamp;
this.value = value;
}
// 重写HashCode方法
public int hashCode() {
return timestamp + value.hashCode();
}
// 重写equals方法
public boolean equals(Object obj) {
if (obj instanceof Pair) {
Pair pair = (Pair) obj;
return this.timestamp == pair.timestamp && this.value.equals(pair.value);
}
return false;
}
// 继承Comparable接口必须重写compareTo方法制定比较规则
@Override
public int compareTo(Pair pair) {
if (this.timestamp != pair.timestamp) {
return timestamp - pair.timestamp;
} else {
return this.value.compareTo(pair.value);
}
}
}
Map<String, List<Pair>> map;
/**
* 将value和timetamp存放在Pair中,方便get得到之前的value
* 将key和Pair放入Map中方便通过key寻找value匹配组
*/
public TimeMap() {
map = new HashMap<String, List<Pair>>();
}
public void set(String key, String value, int timestamp) {
List<Pair> pairs = map.getOrDefault(key, new ArrayList<Pair>());
pairs.add(new Pair(timestamp, value));
map.put(key, pairs);
}
// 返回上一个时间戳的value
public String get(String key, int timestamp) {
List<Pair> pairs = map.getOrDefault(key, new ArrayList<Pair>());
// 默认最大字符串,保证只能得到之前的时间戳
Pair pair = new Pair(timestamp, String.valueOf((char) 127));
int i = binarySearch(pairs, pair);
if (i > 0) {
return pairs.get(i - 1).value;
}
return "";
}
// 二分查找以前set过的value
// 用重写的compareTo比较得到相同value的下一个时间戳
// 如果没有同value的时间戳,二分查找上一个value
private int binarySearch(List<Pair> pairs, Pair target) {
int low = 0, high = pairs.size() - 1;
if (high < 0 || pairs.get(high).compareTo(target) <= 0) {
return high + 1;
}
while (low < high) {
int mid = (high - low) / 2 + low;
Pair pair = pairs.get(mid);
if (pair.compareTo(target) <= 0) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
}
总结
第一次接触到Pair(匹配),java也自带了简单的Pair类型,只有简单的key和valu属性
pair和Map都是键值对结构,不过Pair适用于key和value都具有各自意义的情况,灵活性更高
并且继承自定义的比较方法的Comparable接口需要重写compareTo方法,一个设计两种结构
如果文章存在问题或者有更好的题解,欢迎在评论区斧正和评论,各自努力,你我最高处见