问题描述
设计一个基于时间的键值数据结构,该结构可以在不同时间戳存储对应同一个键的多个值,并针对特定时间戳检索键对应的值。
实现
TimeMap
类:
TimeMap()
初始化数据结构对象void set(String key, String value, int timestamp)
存储给定时间戳timestamp
时的键key
和值value
。String get(String key, int timestamp)
返回一个值,该值在之前调用了set
,其中timestamp_prev <= timestamp
。如果有多个这样的值,它将返回与最大timestamp_prev
关联的值。如果没有值,则返回空字符串(""
)。
示例
示例 1:
输入: ["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 <= 10^7
set
操作中的时间戳timestamp
都是严格递增的- 最多调用
set
和get
操作2 * 10^5
次
问题分析:
一开始,想着动用最少的脑子来解决这道中等题,直接暴力,下面先贴暴力原码。主要思想是拿timestamp为key,对应的键值对存储成pair进数组里,然后for循环遍历timestamp根据对应的key找到value。直接狠狠的超时。
然后意识到get方法的时间复杂度过高,众所周知,在查找过程中只要超时,先想想是不是自己太菜(bushi),应该第一时间想想能不能优化成二分。所以直接采用map,利用内置的lower_bound函数,具体流程直接看代码。
代码如下:
""暴力"",过了44/51的案例
class TimeMap {
public:
map<int, vector<pair<string, string>>> mp;
TimeMap() {
}
void set(string key, string value, int timestamp) {
mp[timestamp].push_back({key, value});
}
string get(string key, int timestamp) {
if(mp.find(timestamp) == mp.end())
{
string final = "";
for(auto &[x, v] : mp)
{
if(x <= timestamp)
for(int i = 0; i < v.size(); i ++)
{
if(v[i].first == key)
final = v[i].second;
}
}
return final;
}
else
{
for(auto &v : mp[timestamp])
{
if(v.first == key)
{
return v.second;
}
}
}
return "";
}
};
""二分""
class TimeMap {
public:
//意识到我们最终要在查找timestamp上花时间,直接用map存确保有序
unordered_map<string, map<int, string>> mp;
TimeMap() {
}
void set(string key, string value, int timestamp) {
mp[key][timestamp] = value; // 由于map是有序的,因此不需要vector,而值直接绑定到时间戳
}
string get(string key, int timestamp) {
//查找mp里是否有key对应的value
auto it = mp.find(key);
//找到了
if(it != mp.end()) {
// 使用lower_bound找到存储中大于或等于timestamp的最小时间戳
auto valueIt = it->second.lower_bound(timestamp);
// 如果准确找到,则返回对应值
if(valueIt != it->second.end() && valueIt->first == timestamp) {
return valueIt->second;
}
// 如果没有准确找到且不是首元素,找到第一个较大的元素,进行回退
if(valueIt != it->second.begin()) {
valueIt--;
return valueIt->second; // 返回小于时间戳的最近一次的值
}
}
return ""; // 如果找不到元素,则返回空字符串
}
};