本文正在參加「Python主題月」,詳情查看 活動連接html
這是 LeetCode 上的 981. 基於時間的鍵值存儲 ,難度爲 中等。node
Tag : 「設計數據結構」、「哈希表」、「數組」、「紅黑樹」git
建立一個基於時間的鍵值存儲類 TimeMap
,它支持下面兩個操做:github
示例 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:markdown
輸入: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"]
複製代碼
提示:數據結構
120000
次。因爲 timestamp
是嚴格遞增,且沒有刪除 KV 的操做。app
咱們可使用哈希表套數組的方式進行實現,從而達到均攤 的插入操做和 的查詢操做。dom
具體的,爲了方便理解,咱們能夠先建一個 Node
類,類中包含鍵值對和時間戳信息。函數
而後使用一個全局哈希表 map
記錄某個 key
對應了哪些 Node
。其中多個 Node
是以動態數組的形式進行「以 timestamp
升序」存儲:
set
操做:以
的複雜度找到某個 key
對應的數組,利用 timestamp
嚴格遞增的特性,以
複雜度將新 Node
加入當前數組尾部;get
操做:以
的複雜度找到某個 key
對應的數組,利用 timestamp
嚴格遞增的特性,經過二分以
複雜度找到可能符合條件的 Node
。Java 代碼:
class TimeMap {
class Node {
String k, v;
int t;
Node (String _k, String _v, int _t) {
k = _k; v = _v; t = _t;
}
}
Map<String, List<Node>> map = new HashMap<>();
public void set(String k, String v, int t) {
List<Node> list = map.getOrDefault(k, new ArrayList<>());
list.add(new Node(k, v, t));
map.put(k, list);
}
public String get(String k, int t) {
List<Node> list = map.getOrDefault(k, new ArrayList<>());
if (list.isEmpty()) return "";
int n = list.size();
int l = 0, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (list.get(mid).t <= t) {
l = mid;
} else {
r = mid - 1;
}
}
return list.get(r).t <= t ? list.get(r).v : "";
}
}
複製代碼
Python 3 代碼:
class TimeMap:
def __init__(self):
""" Initialize your data structure here. """
self.map = defaultdict(list)
def set(self, key: str, value: str, timestamp: int) -> None:
self.map[key].append((key,value,timestamp))
def get(self, key: str, timestamp: int) -> str:
if key not in self.map:
return ""
lt = self.map[key]
n = len(lt)
l, r = 0, n - 1
while l < r:
mid = l + r + 1 >> 1
if lt[mid][2] <= timestamp:
l = mid
else:
r = mid - 1
return lt[r][1] if lt[r][2] <= timestamp else ""
複製代碼
set
操做的複雜度爲
;get
操做的複雜度爲
若是增長 del
操做呢?咱們須要作出何種調整?
考慮在原題的基礎上,增長一個 String del(String k, int t)
的功能:將嚴格等於鍵和時間戳的 KV 對刪掉。
因爲存在刪除 KV 的動做,咱們須要將實現從「哈希表套數組」改爲「哈希表套樹」,這裏直接使用基於紅黑樹實現的 TreeMap
便可。
同時爲了驗證刪除邏輯的正確性,咱們在 get
動做發生前,先產生一次隨機性的刪除,刪除後又從新插入。
Java 代碼:
class TimeMap {
class Node {
String k, v;
int t;
Node (String _k, String _v, int _t) {
k = _k; v = _v; t = _t;
}
}
Map<String, TreeMap<Integer, Node>> map = new HashMap<>();
public void set(String k, String v, int t) {
update(k, t);
TreeMap<Integer, Node> ts = map.getOrDefault(k, new TreeMap<Integer, Node>());
ts.put(t, new Node(k, v, t));
map.put(k, ts);
}
Node _get(String k, int t) {
TreeMap<Integer, Node> ts = map.get(k);
if (ts == null) return null;
Map.Entry<Integer, Node> entry = ts.floorEntry(t);
if (entry == null) return null;
Node node = entry.getValue();
return node;
}
public String get(String k, int t) {
randomDel();
Node node = _get(k, t);
return node != null && node.t <= t ? node.v : "";
}
public String del(String k, int t) {
TreeMap<Integer, Node> ts = map.get(k);
if (ts == null) return null;
Map.Entry<Integer, Node> entry = ts.floorEntry(t);
if (entry == null) return null;
Node node = entry.getValue();
if (node != null && node.t == t) {
ts.remove(t);
return node.v;
}
return "";
}
List<String> allInfo = new ArrayList<>();
Random random = new Random();
// 保存全部的 kt 信息
void update(String k, int t) {
String nk = k + "_" + t;
allInfo.add(nk);
}
// 隨機刪除,再從新插入,驗證代碼正確性
void randomDel() {
int idx = random.nextInt(allInfo.size());
String[] ss = allInfo.get(idx).split("_");
String k = ss[0];
int t = Integer.parseInt(ss[1]);
Node node = _get(k, t);
del(node.k, node.t);
set(node.k, node.v, node.t);
}
}
複製代碼
Python 3 代碼:
from sortedcontainers import SortedDict
class TimeMap:
def __init__(self):
""" Initialize your data structure here. """
self.map = defaultdict(SortedDict)
def set(self, key: str, value: str, timestamp: int) -> None:
self.map[key][timestamp] = value
def get(self, key: str, timestamp: int) -> str:
if key not in self.map:
return ""
keys = self.map[key].keys()
idx = self.map[key].bisect_left(timestamp)
if idx == len(keys) or keys[idx] > timestamp:
idx -= 1
return self.map[key][keys[idx]] if idx >= 0 else ""
複製代碼
set
操做的複雜度爲
;get
操做會完成隨機刪除/從新插入/查詢的動做,複雜度均爲爲
,整個 get
的複雜度還是
(只是常數變大了)這是咱們「刷穿 LeetCode」系列文章的第 No.981
篇,系列開始於 2021/01/01,截止於起始日 LeetCode 上共有 1916 道題目,部分是有鎖題,咱們將先把全部不帶鎖的題目刷完。
在這個系列文章裏面,除了講解解題思路之外,還會盡量給出最爲簡潔的代碼。若是涉及通解還會相應的代碼模板。
爲了方便各位同窗可以電腦上進行調試和提交代碼,我創建了相關的倉庫:github.com/SharingSour… 。
在倉庫地址裏,你能夠看到系列文章的題解連接、系列文章的相應代碼、LeetCode 原題連接和其餘優選題解。