1. 程式人生 > >[LeetCode] All O`one Data Structure 全O(1)的資料結構

[LeetCode] All O`one Data Structure 全O(1)的資料結構

Implement a data structure supporting the following operations:

  1. 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.
  2. 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.
  3. GetMaxKey() - Returns one of the keys with maximal value. If no element exists, return an empty string "".
  4. 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.

這道題讓我們實現一個全是O(1)複雜度的資料結構,包括了增加key,減少key,獲取最大key,獲取最小key,這幾個函式。由於需要常數級的時間複雜度,我們首先第一反應就是要用雜湊表來做,不僅如此,我們肯定還需要用list來儲存所有的key,那麼雜湊表就是建立key和list中位置迭代器之間的對映,這不由得令人想到了之前那道

LRU Cache,也是用了類似的方法來解,但是感覺此題還要更加複雜一些。由於每個key還要對應一個次數,所以list中不能只放key,而且相同的次數可能會對應多個key值,所以我們用unordered_set來儲存次數相同的所有key值,我們建立一個Bucket的結構體來儲存次數val,和儲存key值的集合keys。解題思路主要參考了網友ivancjw的帖子,資料結構參考了史蒂芬大神的帖子,思路是,我們建立一個次數分層的結構,次數多的在頂層,每一層放相同次數的key值,例如下面這個例子:

"A": 4, "B": 4, "C": 2, "D": 1

那麼用我們設計的結構儲存出來就是:

row0: val = 4, keys = {"A", "B"}
row1: val = 2, keys = {"C"}
row2: val = 1, keys = {"D"}

好,我們現在來分析如何實現inc函式,我們來想,如果我們插入一個新的key,跟我們插入一個已經存在的key,情況是完全不一樣的,那麼我們就需要分情況來討論:

- 如果我們插入一個新的key,那麼由於該key沒有出現過,所以加入後次數一定為1,那麼就有兩種情況了,如果list中沒有val為1的這一行,那麼我們需要插入該行,如果已經有了val為1的這行,我們直接將key加入集合keys中即可。

- 如果我們插入了一個已存在的key,那麼由於個數增加了1個,所以該key值肯定不能在當前行繼續待下去了,要往上升職啊,那麼這裡就有兩種情況了,如果該key要升職到的那行不存在,我們需要手動新增那一行;如果那一行存在,我們之間將key加入集合keys中,記得都要將原來行中的key值刪掉。

下面我們再來看dec函式如何實現,其實理解了上面的inc函式,那麼dec函式也就沒什麼難度了:

- 如果我們要刪除的key不存在,那麼直接返回即可。

- 如果我們要刪除的key存在,那麼我們看其val值是否為1,如果為1的話,那麼直接在keys中刪除該key即可,然後還需要判斷如果該key是集合中的唯一一個,那麼該行也需要刪除。如果key的次數val不為1的話,我們要考慮降級問題,跟之前的升職很類似,如果要降級的行不存在,我們手動新增上,如果存在,則直接將key值新增到keys集合中即可。

當我們搞懂了inc和dec的實現方法,那麼getMaxKey()和getMinKey()簡直就是福利啊,不要太簡單啊,直接返回首層和尾層的key值即可,參見程式碼如下:

class AllOne {
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 (!m.count(key)) {
            if (buckets.empty() || buckets.back().val != 1) {
                auto newBucket = buckets.insert(buckets.end(), {1, {key}});
                m[key] = newBucket;
            } else {
                auto newBucket = --buckets.end();
                newBucket->keys.insert(key);
                m[key] = newBucket;
            }
        } else {
            auto curBucket = m[key], lastBucket = (--m[key]);
            if (lastBucket == buckets.end() || lastBucket->val != curBucket->val + 1) {
                auto newBucket = buckets.insert(curBucket, {curBucket->val + 1, {key}});
                m[key] = newBucket;
            } else {
                lastBucket->keys.insert(key);
                m[key] = lastBucket;
            }
            curBucket->keys.erase(key);
            if (curBucket->keys.empty()) buckets.erase(curBucket);
        }
    }
    
    /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
    void dec(string key) {
        if (!m.count(key)) return;
        auto curBucket = m[key];
        if (curBucket->val == 1) {
            curBucket->keys.erase(key);
            if (curBucket->keys.empty()) buckets.erase(curBucket);
            m.erase(key);
            return;
        }
        auto nextBucket = ++m[key];
        if (nextBucket == buckets.end() || nextBucket->val != curBucket->val - 1) {
            auto newBucket = buckets.insert(nextBucket, {curBucket->val - 1, {key}});
            m[key] = newBucket;
        } else {
            nextBucket->keys.insert(key);
            m[key] = nextBucket;
        }
        curBucket->keys.erase(key);
        if (curBucket->keys.empty()) buckets.erase(curBucket);
    }
    
    /** Returns one of the keys with maximal value. */
    string getMaxKey() {
        return buckets.empty() ? "" : *(buckets.begin()->keys.begin());
    }
    
    /** Returns one of the keys with Minimal value. */
    string getMinKey() {
        return buckets.empty() ? "" : *(buckets.rbegin()->keys.begin());
    }
private:
    struct Bucket { int val; unordered_set<string> keys; };
    list<Bucket> buckets;
    unordered_map<string, list<Bucket>::iterator> m;
};

類似題目:

參考資料: