Implement a data structure supporting the following operations:
Inc(Key) - Inserts a new key with value 1. Or increments an existing key by 1. Key is guaranteed to be a non-empty string.
Dec(Key) - If Key’s value is 1, remove it from the data structure. Otherwise decrements an existing key by 1. If the key does not exist, this function does nothing. Key is guaranteed to be a non-empty string.
GetMaxKey() - Returns one of the keys with maximal value. If no element exists, return an empty string “”.
GetMinKey() - Returns one of the keys with minimal value. If no element exists, return an empty string “”.
Challenge: Perform all these in O(1) time complexity.
方法1:
discussion: https://leetcode.com/problems/all-oone-data-structure/discuss/91398/C%2B%2B-solution-with-comments
思路:
为了所有操作都是O(1),采用按照val把key划分成子集的结构,用一个struct Bucket来装{val, keys},bucket之间采用list的结构连接起来。这样在getmin/getmax时,可以直接取头节点/尾节点子集中任意key。为了能inc和dec也达到O(1),需要一个hash来存所有的key,并且能够按要求将list中的key移动到新的bucket,为此,hash中的value是key此时所在的bucket的指针。如果在增加/减小之后不存在对应的bucket,就需要在list中添加,耗时也是O(1)。将key加到新的bucket当中,并从之前的bucket中删除。此时如果之前的bucket变空,需要从list中删掉这个节点。
易错点
- iterator不可以+1,但是可以++。
- 要判断next == buckets.end(), 和bucket == buckets.begin()的特殊情况,否则会heap overflow。
- 删除时如果头节点的val = 1, 从这里删除不需要建立新bucket,但是仍然需要从hash中把对应的key删掉。
- *(buckets.rbegin() -> keys.begin()):要解指针
class AllOne {
private:
struct Bucket {
int val;
unordered_set<string> keys;
};
list<Bucket> buckets;
unordered_map<string, list<Bucket>::iterator> hash;
public:
/** Initialize your data structure here. */
AllOne() {
}
/** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
void inc(string key) {
if (!hash.count(key)) {
buckets.insert(buckets.begin(), {0, {key}});
hash[key] = buckets.begin();
}
auto next = hash[key], bucket = next++;
// if end of the list, or not consecutive vals, construct a new bucket
if (next == buckets.end() || next -> val > bucket -> val + 1) {
next = buckets.insert(next, {bucket -> val + 1, {}});
}
next -> keys.insert(key);
hash[key] = next;
// remove key from the old bucket, if the bucket is empty, remove from the buckets list
bucket -> keys.erase(key);
if (bucket -> keys.empty()) {
buckets.erase(bucket);
}
}
/** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
void dec(string key) {
if (!hash.count(key)) return;
auto prev = hash[key], bucket = prev--;
hash.erase(key);
// if bucket -> val == 1, don't neet to create new prev bucket
if (bucket -> val > 1){
// if no prev bucket or gap with prev bucket
if (bucket == buckets.begin() || prev -> val < bucket -> val - 1) {
prev = buckets.insert(bucket, {bucket -> val - 1, {}});
}
prev -> keys.insert(key);
hash[key] = prev;
}
// remove key from the old bucket, if the bucket is empty, remove from the buckets list
bucket -> keys.erase(key);
if (bucket -> keys.empty()) {
buckets.erase(bucket);
}
}
/** Returns one of the keys with maximal value. */
string getMaxKey() {
return buckets.empty() ? "" : *(buckets.rbegin() -> keys.begin());
}
/** Returns one of the keys with Minimal value. */
string getMinKey() {
return buckets.empty() ? "" : *(buckets.begin() -> keys.begin());
}
};