Leetcode 380. 常数时间插入、删除和获取随机元素

题目描述:

设计一个支持在平均 时间复杂度 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();

解题思路:

分析:题目的难点在于有delete操作的情况下,要保证getRandom( )等概率随机返回集合中的一个元素。

一般地,题目的对时间复杂度的要求越高,都需要使用更多的辅助结构,以“空间换时间”。这里可以采用“两个哈希表”(多一个哈希表)或者“一个哈希表加一个数组”(多一个数组)。

渐进思路

(1)没有delete(val),只有insert(val)和getRandom( )操作的情况下,连续的插入元素键值对<key,index>,因为index在逻辑上是连续,因此getRandom()等概率随机返回集合中的一个元素很容易实现,rand() % index (0~index-1)即可;

(2)有delete(val)操作,可以删除元素键值对之后,使得index不连续,中间有空洞,所以此时getRandom()产生的index可能正好是被删除的,导致时间复杂度超过O(1),所以delete(val)操作需要有一些限定条件,即保证每删除一个元素键值对之后,index个数减一,但是整体index在逻辑上是连续的。

例如:0~5 ——> 0~4  ——> 0~3

代码里关键部分有注释。

class RandomizedSet {
public:
    /** Initialize your data structure here. */

    //建立两个hash表,一个是<key,index>,另一个是<index,key>;
    RandomizedSet() {

    }

    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    bool insert(int val) {
        //元素不存在时,插入
        if(keyIndexMap.count(val) == 0)
        {
            keyIndexMap[val] = size;
            indexKeyMap[size] = val;
            ++size;
            return true;
        }
        return false;
    }

    /** Removes a value from the set. Returns true if the set contained the specified element. */
    bool remove(int val) {
        ////每删除一个键值对,用最后的键值对填充该空位,保证整个index在逻辑上连续,size减一
        if(keyIndexMap.count(val) == 1)
        {
            int removeIndex = keyIndexMap[val];
            int lastIndex = --size;//若size=1000,表示0~999;这里是取最后一个index

            int lastKey = indexKeyMap[lastIndex];
            keyIndexMap[lastKey] = removeIndex;
            indexKeyMap[removeIndex] = lastKey;

            keyIndexMap.erase(val);
            indexKeyMap.erase(lastIndex);//下标方式取val对应的值index

            return true;
        }
        return false;
    }

    /** Get a random element from the set. */
    int getRandom() {
        if (size == 0) {
                return NULL;
            }
        //srand((unsigned)time(NULL));  //去掉srand(),保证稳定的产生随机序列
        int randomIndex = (int) (rand() % size); // 0 ~ size -1
        return indexKeyMap[randomIndex];
    }

private:
    map<int,int> keyIndexMap;
    map<int,int> indexKeyMap;
    int size = 0;
};

/**
 * 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();
 */

用时更少的范例:

这是Leetcode官网上C++完成此题提高的用时排名靠前的代码,这里与上面的解法差异就在于额外的辅助结构的选择,这里选的是在哈希表的基础上多增加一个数组,数组操作的时间复杂度和哈希表操作的时间复杂度均为O(1),但是数组时间复杂度O(1)的常数项更小,因此,这种解法效率更高。

class RandomizedSet {
public:
    /** Initialize your data structure here. */
    RandomizedSet() {

    }

    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    bool insert(int val) {
        if (m.count(val)) return false;
        nums.push_back(val);
        m[val] = nums.size() - 1;
        return true;
    }

    /** Removes a value from the set. Returns true if the set contained the specified element. */
    bool remove(int val) {
        if (!m.count(val)) return false;
        int last = nums.back();
        m[last] = m[val];
        nums[m[val]] = last;
        nums.pop_back();
        m.erase(val);
        return true;
    }

    /** Get a random element from the set. */
    int getRandom() {
        return nums[rand() % nums.size()];
    }
//private:
//注释掉private,提高一点速度
    vector<int> nums;
    unordered_map<int, int> m;
};

/**
 * 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();
 */

原文地址:https://www.cnblogs.com/paulprayer/p/9927564.html

时间: 2024-10-18 19:51:30

Leetcode 380. 常数时间插入、删除和获取随机元素的相关文章

[leetcode]380. Insert Delete GetRandom O(1)常数时间插入删除取随机值

Design a data structure that supports all following operations in average O(1) time. insert(val): Inserts an item val to the set if not already present. remove(val): Removes an item val from the set if present. getRandom: Returns a random element fro

[LeetCode] Insert Delete GetRandom O(1) 常数时间内插入删除和获得随机数

Design a data structure that supports all following operations in average O(1) time. insert(val): Inserts an item val to the set if not already present. remove(val): Removes an item val from the set if present. getRandom: Returns a random element fro

[LeetCode] Insert Delete GetRandom O(1) - Duplicates allowed 常数时间内插入删除和获得随机数 - 允许重复

Design a data structure that supports all following operations in average O(1) time. Note: Duplicate elements are allowed. insert(val): Inserts an item val to the collection. remove(val): Removes an item val from the collection if present. getRandom:

leetcode第27题:删除vector数组的元素(array)

题目: Given an array and a value, remove all instances of that value in place and return the new length. The order of elements can be changed. It doesn't matter what you leave beyond the new length. 这个比较简单,掌握vector的用法即可. 1 int removeElement(vector<int>

顺序表 初始化 插入 删除 查找 合并 交换 判断为空 求长度

#include <stdio.h> #include <stdlib.h> #define OK 1 #define TRUE 1 #define ERROR -1 #define FALSE -1 #define OVERFLOW -2 #define ElemType int #define Status int typedef int ElemType typedef int Status #define LEN sizeof(SqList) #define MLC (Li

静态链表 初始化 定位 Malloc Free 插入 删除

#include <stdio.h> #include <stdlib.h> #define OK 1 #define TRUE 1 #define ERROR -1 #define FALSE -1 #define OVERFLOW -2 #define ElemType int #define Status int typedef int ElemType typedef int Status #define MAX_SIZE 1000;//表最大空间 /* //线性表的基本操

AVL树非递归插入删除思路

AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增加和删除可能需要通过一次或多次树旋转来重新平衡这个树.AVL树得名于它的发明者G.M. Adelson-Velsky和E.M. Landis,他们在1962年的论文<An algorithm for the organization of information>中发表了它. 节点的平衡因子是它的左子树的高度减去它的右子树的

转 :asp教程.net c#数组遍历、排序、删除元素、插入、随机元素 数组遍历

asp教程.net c#数组遍历.排序.删除元素.插入.随机元素数组遍历 short[] sts={0,1,100,200};for(int i=0;i<sts.lenght;i++){  if(sts[i]>50) {  .....  }} 数组随机元素 public  hashtable  noorder(int count)         {             arraylist mylist = new arraylist();             hashtable ha

JavaScript之jQuery-3 jQuery操作DOM(查询、样式操作、遍历节点、创建插入删除、替换、复制)

一.jQuery操作DOM - 查询 html操作 - html(): 读取或修改节点的HTML内容,类似于JavaScript中的innerHTML属性 文本操作 - text(): 读取或修改节点的文本内容,类似于JavaScript中的textContent属性 值操作 - val(): 读取或修改节点的value属性值,类似于 JavaScript 中的value值 属性操作 - attr(): 读取或者修改节点的属性 - removeAttr(): 删除节点的属性 二.jQuery操作