LeetCode-432 全O(1)的資料結構
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/all-oone-data-structure
題目描述
請你設計一個用於儲存字串計數的資料結構,並能夠返回計數最小和最大的字串。
實現 AllOne 類:
AllOne() 初始化資料結構的物件。
inc(String key) 字串 key 的計數增加 1 。如果資料結構中尚不存在 key ,那麼插入計數為 1 的 key 。
dec(String key) 字串 key 的計數減少 1 。如果 key 的計數在減少後為 0 ,那麼需要將這個 key 從資料結構中刪除。測試用例保證:在減少計數前,key 存在於資料結構中。
getMaxKey() 返回任意一個計數最大的字串。如果沒有元素存在,返回一個空字串 "" 。
getMinKey() 返回任意一個計數最小的字串。如果沒有元素存在,返回一個空字串 "" 。
示例:
輸入
["AllOne", "inc", "inc", "getMaxKey", "getMinKey", "inc", "getMaxKey", "getMinKey"]
[[], ["hello"], ["hello"], [], [], ["leet"], [], []]
輸出
[null, null, null, "hello", "hello", null, "hello", "leet"]
解釋
AllOne allOne = new AllOne();
allOne.inc("hello");
allOne.inc("hello");
allOne.getMaxKey(); // 返回 "hello"
allOne.getMinKey(); // 返回 "hello"
allOne.inc("leet");
allOne.getMaxKey(); // 返回 "hello"
allOne.getMinKey(); // 返回 "leet"
提示:
1 <= key.length <= 10
key 由小寫英文字母組成
測試用例保證:在每次呼叫 dec 時,資料結構中總存在 key
最多呼叫 inc、dec、getMaxKey 和 getMinKey 方法 5 * 104 次
解題思路
題目難點在於要將操作的時間複雜度全部變為O(1),對於修改來說,很容易想到雜湊表,但是雜湊表的查詢最大值最小值的時間複雜度並不是O(1),查詢最大值和最小值的時間複雜度為O(1)的很容易想到優先佇列,但是優先佇列的修改時間複雜度並不是O(1),所以,這裡使用一個遞增的雙向連結串列來記錄所有資料,當查詢最小值返回連結串列頭,查詢最大值返回連結串列尾,並且通過雜湊表將連結串列結點和key值繫結,使得修改時候可以在O(1)時間內找到結點,結點內容儲存的是一個set,這樣可以將相同計數的結點合成一個結點,將修改的過程縮短到O(1)的時間複雜度。注意在修改時候,分類處理未出現過的key和已出現的key,還有計數為0的key。
程式碼展示
class AllOne { public: list<pair<unordered_set<string>, int>> NodeList; unordered_map<string, list<pair<unordered_set<string>, int>>::iterator> mapstriterMap; AllOne() { } void inc(string key) { if(mapstriterMap.count(key)) { auto curIter = mapstriterMap[key]; auto nextIter = next(curIter); if(nextIter == NodeList.end() || curIter->second + 1 < nextIter->second) { unordered_set<string> node{key}; mapstriterMap[key] = NodeList.emplace(nextIter, node, curIter->second + 1); } else { nextIter->first.emplace(key); mapstriterMap[key] = nextIter; } curIter->first.erase(key); if(curIter->first.empty()) NodeList.erase(curIter); } else { if(NodeList.empty() || NodeList.begin()->second != 1) { unordered_set<string> node{key}; NodeList.emplace_front(node, 1); } else { NodeList.begin()->first.emplace(key); } mapstriterMap[key] = NodeList.begin(); } } void dec(string key) { if(mapstriterMap.count(key)) { auto curIter = mapstriterMap[key]; auto prevIter = prev(curIter); if(curIter == NodeList.begin()) { if(curIter->second - 1 <= 0) { mapstriterMap.erase(key); } else { unordered_set<string> node{key}; NodeList.emplace_front(node, curIter->second - 1); mapstriterMap[key] = NodeList.begin(); } } else if(curIter->second - 1 > prevIter->second) { unordered_set<string> node{key}; mapstriterMap[key] = NodeList.emplace(curIter, node, curIter->second - 1); } else { prevIter->first.emplace(key); mapstriterMap[key] = prevIter; } curIter->first.erase(key); if(curIter->first.empty()) NodeList.erase(curIter); } } string getMaxKey() { return NodeList.empty()? "" : *NodeList.rbegin()->first.begin(); } string getMinKey() { return NodeList.empty()? "" : *NodeList.begin()->first.begin(); } }; /** * Your AllOne object will be instantiated and called as such: * AllOne* obj = new AllOne(); * obj->inc(key); * obj->dec(key); * string param_3 = obj->getMaxKey(); * string param_4 = obj->getMinKey(); */
執行結果