面试算法30:插入、删除和随机访问都是O(1)的容器

题目

设计一个数据结构,使如下3个操作的时间复杂度都是O(1)。

  • insert(value):如果数据集中不包含一个数值,则把它添加到数据集中。
  • remove(value):如果数据集中包含一个数值,则把它删除。
  • getRandom():随机返回数据集中的一个数值,要求数据集中每个数字被返回的概率都相同。

分析

由于题目要求插入和删除(包括判断数据集中是否包含一个数值)的时间复杂度都是O(1),能够同时满足这些时间效率要求的只有哈希表,因此这个数据结构要用到哈希表。但是如果只用哈希表,则不能等概率地返回其中的每个数值。

如果数值是保存在数组中的,那么很容易实现等概率返回数组中的每个数值。假设数组的长度是n,那么等概率随机生成从0到n-1的一个数字。如果生成的随机数是i,则返回数组中下标为i的数值。由此可以发现,需要结合哈希表和数组的特性来设计这个数据容器。

由于数值保存在数组中,因此需要知道每个数值在数组中的位置,否则在删除的时候就必须顺序扫描整个数组才能找到待删除的数值,那就需要O(n)的时间。通常把每个数值在数组中的位置信息保存到一个HashMap中,HashMap的键是数值,而对应的值为它在数组中的位置。

java 复制代码
public class Test {
    public static void main(String[] args) {
        RandomizedSet randomizedSet = new RandomizedSet();
        randomizedSet.insert(1);
        randomizedSet.insert(2);
        randomizedSet.insert(3);
        randomizedSet.insert(4);
        for (int i = 0; i < randomizedSet.nums.size(); i++) {
            System.out.println(randomizedSet.nums.get(i));
        }
        System.out.println("-----------------------");
        randomizedSet.remove(2);
        for (int i = 0; i < randomizedSet.nums.size(); i++) {
            System.out.println(randomizedSet.nums.get(i));
        }
        System.out.println("-----------------------");
        System.out.println(randomizedSet.getRandom());
    }

    static class RandomizedSet {
        HashMap<Integer, Integer> numToLocation;
        ArrayList<Integer> nums;

        public RandomizedSet() {
            numToLocation = new HashMap<>();
            nums = new ArrayList<>();
        }

        public boolean insert(int val) {
            if (numToLocation.containsKey(val)) {
                return false;
            }

            numToLocation.put(val, nums.size());
            nums.add(val);
            return true;
        }

        public boolean remove(int val) {
            if (!numToLocation.containsKey(val)) {
                return false;
            }

            int location = numToLocation.get(val);

            numToLocation.put(nums.get(nums.size() - 1), location);
            numToLocation.remove(val);
            nums.set(location, nums.get(nums.size() - 1));
            nums.remove(nums.size() - 1);
            return true;
        }

        public int getRandom() {
            Random random = new Random();
            int r = random.nextInt(nums.size());
            return nums.get(r);
        }
    }
}
相关推荐
liulilittle7 分钟前
反量化:反量化Q8零(q8_0)
人工智能·算法·机器学习·ai·llm
青 春 记 忆6 小时前
LeetCode 21. 合并两个有序链表|Python 解法详解
python·算法·leetcode·链表
Sagittarius_A*9 小时前
后量子密码|通识认知 03|量子威胁辟谣:Grover 算法对对称密码的影响与误区
算法·信息安全·密码学·量子计算·pqc·后量子密码
Patrick在香港9 小时前
Python Docker镜像从1.2GB到89MB:多阶段构建的完整优化实录
java·python·docker·信息可视化·容器·数据分析·ai编程
上海安当技术9 小时前
敏感数据怎么防拖库?信封加密(DEK+KEK 二层密钥)架构设计与 Java 实战
java·开发语言·python
我是唐青枫9 小时前
Java JCommander 实战详解:用注解解析命令行参数
java·开发语言
大鹏说大话9 小时前
C++ 内存布局详解:类、虚函数、虚表底层原理
java·开发语言
还是鼠鼠10 小时前
Spring AI连接DeepSeek与通义千问:application.yaml配置详解
java·通义千问·spring ai·deepseek
QAQo7T10 小时前
Python组蓝桥杯备赛超详细知识点总结笔记_排序算法篇
笔记·python·算法·蓝桥杯·排序算法
千里之行,始于足下sanhai10 小时前
P8686 [蓝桥杯 2019 省 A] 修改数组 - 数字魔法大冒险 题解
c++·算法·动态规划