LeetCode 380: 常数时间插入、删除和获取随机元素 Insert Delete GetRandom O(1)-阿里云开发者社区

开发者社区> 爱写Bug> 正文

LeetCode 380: 常数时间插入、删除和获取随机元素 Insert Delete GetRandom O(1)

简介: 题目: 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。 insert(val):当元素 val 不存在时,向集合中插入该项。 remove(val):元素 val 存在时,从集合中移除该项。
+关注继续查看

题目:

设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。

  1. insert(val):当元素 val 不存在时,向集合中插入该项。
  2. remove(val):元素 val 存在时,从集合中移除该项。
  3. getRandom:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。

Design a data structure that supports all following operations in average O(1) time.

  1. insert(val): Inserts an item val to the set if not already present.
  2. remove(val): Removes an item val from the set if present.
  3. getRandom: Returns a random element from current set of elements. Each element must have the same probability of being returned.

示例 :

// 初始化一个空的集合。
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();

解题思路:

  • 要求时间复杂度 O(1) 插入删除操作: 可以从零开始设计一个哈希算法, 也可以借助高级程序语言中已设计好的哈希集合/映射
  • 要求相同概率随随机返回元素: 哈希集合无法做到随机返回一个元素, 可以再借助一个顺序存储如数组, 随机产生索引下标, 返回对应元素值

那么就需要用哈希映射存储元素, key 为元素值, value 为元素存储在辅助数组中的索引下标值

插入操作就是数组, 哈希映射的插入操作

难点在于删除操作, 首先删除哈希映射中的该键值对, 其次删除数组中的该元素值, 不能简单的通过赋一个不可能出现的数值伪删除, 因为这种伪删除会导致数组越来越大撑爆内存。

代码:

Java:

class RandomizedSet {
    List<Integer> list;
    Map<Integer, Integer> map;
    Random rand;

    /** Initialize your data structure here. */
    public RandomizedSet() {
        list = new ArrayList();
        map = new HashMap();
        rand = new Random();
    }

    /**
     * Inserts a value to the set. Returns true if the set did not already contain the specified element.
     * 常规插入操作
     */
    public boolean insert(int val) {
        if (map.containsKey(val))
            return false;
        map.put(val, list.size()); // value 表示存储在 list 中的索引下标
        list.add(val); // 添加在数组 list 尾部
        return true;
    }

    /**
     * Removes a value from the set. Returns true if the set contained the specified element.
     * 
     */
    public boolean remove(int val) {
        if (!map.containsKey(val)) // 如果哈希映射中不存在该键 直接返回 False
            return false;
        int tmp = list.get(list.size() - 1); // 暂存数组最后一位元素值
        int index = map.get(val); // 获取待删除元素在 list 数组中对应的索引下标 index
        list.set(index, tmp); // 将 list 中该元素值改为暂存的数组最后一位值
        map.put(tmp, index); // 更新哈希映射中代表数组最后一位的键值对 对应的索引下标为 index
        list.remove(list.size() - 1); // 删除数组最后一位
        map.remove(val); // 删除哈希映射中该键值对
        return true;
    }

    /** Get a random element from the set. */
    public int getRandom() {
        return list.get(rand.nextInt(list.size())); // 产生一个大小为 [0, 数组大小) 的随机索引下标
    }
}

Python:

from random import choice

class RandomizedSet:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.val_map = dict()
        self.val_list = list()

    def insert(self, val: int) -> bool:
        """
        Inserts a value to the set. Returns true if the set did not already contain the specified element.
        """
        if val not in self.val_map:
            self.val_map[val] = len(self.val_list) # value 表示存储在 val_list 中的索引下标
            self.val_list.append(val) # 添加在数组 val_list 尾部
            return True
        return False

    def remove(self, val: int) -> bool:
        """
        Removes a value from the set. Returns true if the set contained the specified element.
        """
        if val in self.val_map:
            index = self.val_map[val] # 获取待删除元素在 list 数组中对应的索引下标 index
            last_val = self.val_list[-1] # 暂存数组最后一位元素值
            self.val_list[index] = last_val # 将 list 中该元素值改为暂存的数组最后一位值
            self.val_map[last_val] = index # 更新哈希映射中代表数组最后一位的键值对 对应的索引下标为 index
            self.val_map.pop(val) # 删除哈希映射中该键值对
            self.val_list.pop() # 删除数组最后一位
            return True
        return False

    def getRandom(self) -> int:
        """
        Get a random element from the set.
        """
        return choice(self.val_list) # 返回数组中的随机一个元素

欢迎关注微_信公众号: 爱写Bug

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《jQuery Cookbook中文版》——1.13 克隆DOM元素
jQuery提供clone()方法复制DOM元素。它的用法很简单,只要用jQuery函数选择DOM元素,然后在选择的元素集上调用clone()方法就可以了。结果是返回用于链接的DOM结构的一个副本,而不是原来选中的DOM元素。
938 0
Android 获取Contacts 联系人 姓名 号码 照片信息
直接贴代码不解释 private void getCursors() { Cursor phoneCursor = this.managedQuery( ContactsContract.
456 0
《jQuery Cookbook中文版》——1.14 获取、设置和删除DOM元素属性
除了attr()方法之外,jQuery为使用HTML元素class属性提供了一组很特殊的方法。因为class属性可能包含多个值(例如,class="class1 class2 class3"),所以可以使用这些独特的属性方法管理这些类值。
1049 0
《jQuery Cookbook中文版》——1.12 替换DOM元素
本节书摘来自异步社区《jQuery Cookbook中文版》一书中的第1章,第1.12节,作者:【美】jQuery社区专家组著,更多章节内容可以访问云栖社区“异步社区”公众号查看
1051 0
《jQuery Cookbook中文版》——1.11 删除DOM元素
在使用remove()从DOM中删除选择的元素时,它们并没有从jQuery包装器集中删除。这意味着,从理论上说,可以继续操作它们,甚至可以在必要的时候将它们重新添加到DOM中。
1245 0
jquery 遍历dom元素
$('#navBarID').children()  //选定元素的所有孩子节点 $('#navBarID').children().eq(i) //选定元素的第i个孩子节点,从0计数
858 0
LeetCode 73 Set Matrix Zeroes(设矩阵元素为0)(Array)(*)
版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/52139263 翻译 给定一个mm x nn的矩阵matrix,如果其中一个元素为0,那么将其所在的行和列的元素统统设为0。
812 0
LeetCode 82 Remove Duplicates from Sorted List II(从已排序链表中移除重复元素)(Linked List)(*)
版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/52186216 翻译 给定一个已排序链表,删除所有的重复节点,只保留原始链表中独特的数字。
754 0
+关注
爱写Bug
欢迎关注公众号:爱写Bug
85
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载