LeetCode - 常數時間插入、刪除和獲取隨機元素

題目鏈接:https://leetcode-cn.com/problems/insert-delete-getrandom-o1/

題目描述

設計一個支持在平均 時間複雜度 O(1) 下,執行以下操作的數據結構。

  1. insert(val):當元素 val 不存在時,向集合中插入該項。
  2. remove(val):元素 val 存在時,從集合中移除該項。
  3. getRandom:隨機返回現有集合中的一項。每個元素應該有相同的概率被返回。

示例:

// 初始化一個空的集合。
RandomizedSet randomSet = new RandomizedSet();
// 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomSet.insert(1);
// 返回 false ,表示集合中不存在 2 。
randomSet.remove(2);
// 向集合中插入 2 。返回 true 。集合現在包含 [1,2] 。
randomSet.insert(2);
// getRandom 應隨機返回 1 或 2 。
randomSet.getRandom();
// 從集合中移除 1 ,返回 true 。集合現在包含 [2] 。
randomSet.remove(1);
// 2 已在集合中,所以返回 false 。
randomSet.insert(2);
// 由於 2 是集合中唯一的數字,getRandom 總是返回 2 。
randomSet.getRandom();

思路

  • 使用C++實現的問題就出在如何實現 getRandom() 功能上,需要每個元素返回的概率相同且要求 O(1) 時間複雜度。
  • 由於時間複雜度平均要達到 O(1),因此需要額外建立一個數組來記錄插入的數值,哈希表存的是數組的下標,這樣返回一個隨機值就能夠達到 O(1) 的效率。
  • 插入元素實現簡單,直接更新數組和哈希表即可。但如何在 O(1) 時間刪除元素?哈希表刪除元素自然是 O(1) 時間,但問題是如何 O(1) 時間刪除數組的元素?
  • 這個問題也不難解決,因爲哈希表存着數組的下標。我們可以通過哈希表找到當前待刪元素的下標,然後和數組的末尾元素交換,執行 pop_back 即可。這樣就解決了數組在 O(1) 時間刪除元素的問題。

代碼如下:

class RandomizedSet {
    unordered_map<int, int> dict;
    vector<int> list;
public:
    /** Initialize your data structure here. */
    RandomizedSet() {
        srand(time(0));
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    bool insert(int val) {
        if(dict.find(val) != dict.end()) return false;
        list.push_back(val);
        dict[val] = list.size()-1;
        return true;
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    bool remove(int val) {
        if(dict.find(val) == dict.end()) return false;
        dict[list.back()] = dict[val];
        swap(list.back(), list[dict[val]]);
        list.pop_back();
        dict.erase(val);
        return true;
    }
    
    /** Get a random element from the set. */
    int getRandom() {
        int pos = list.empty() ? 0 : rand() % list.size();
        return list[pos];
    }
};

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet* obj = new RandomizedSet();
 * bool param_1 = obj->insert(val);
 * bool param_2 = obj->remove(val);
 * int param_3 = obj->getRandom();
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章