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


    题目

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

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

    分析

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

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

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

    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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
  • 相关阅读:
    RabbitMQ篇
    Python使用腾讯云SDK实现对象存储(上传文件、创建桶)
    个人博客文章目录索引(持续更新中...)
    浅谈中压系统母线弧光保护的必要性
    Golang一日一库之bcrypt
    PHP 实现 SHA256 with RSA 签名 (实例讲解)
    5-RabbitMQ工作模式-Publish/Subscribe发布与订阅模式
    计算机毕业设计springboot+vue基本微信小程序的学生健康管理小程序
    尚硅谷大数据项目《在线教育之实时数仓》笔记005
    基于 vite 创建 vue3 全家桶项目(vite + vue3 + tsx + pinia)
  • 原文地址:https://blog.csdn.net/GoGleTech/article/details/133908388