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();
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章