• LeetCode——常数时间插入、删除和获取随机元素


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

    • insert(val):当元素 val 不存在时,向集合中插入该项。
    • remove(val):元素 val 存在时,从集合中移除该项。
    • 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();
    

    A:
    我们先来分析一下:对于插入,删除,查找这几个操作,哪种数据结构的时间复杂度是 O(1)?
    HashSet肯定算一个对吧。哈希集合的底层原理就是一个大数组,我们把元素通过哈希函数映射到一个索引上;如果用拉链法解决哈希冲突,那么这个索引可能连着一个链表或者红黑树。

    那么请问对于这样一个标准的HashSet,你能否在 O(1) 的时间内实现getRandom函数?
    其实是不能的,因为根据刚才说到的底层实现,元素是被哈希函数「分散」到整个数组里面的,更别说还有拉链法等等解决哈希冲突的机制,基本做不到 O(1) 时间等概率随机获取元素。

    根据上面的分析,对于getRandom方法,如果想「等概率」且「在 O(1) 的时间」取出元素,一定要满足:底层用数组实现,且数组必须是紧凑的。这样我们就可以直接生成随机数作为索引,从数组中取出该随机索引对应的元素,作为随机元素。

    但如果用数组存储元素的话,插入,删除的时间复杂度怎么可能是 O(1) 呢?
    可以做到!对数组尾部进行插入和删除操作不会涉及数据搬移,时间复杂度是 O(1)。

    所以,如果我们想在 O(1) 的时间删除数组中的某一个元素val,可以先把这个元素交换到数组的尾部,然后再pop掉。交换两个元素必须通过索引进行交换对吧,那么我们需要一个哈希表valToIndex来记录每个元素值对应的索引。

    class RandomizedSet {
        Map<Integer, Integer> index;
        ArrayList<Integer> array;
        Random rand = new Random();
    
        /**
         * Initialize your data structure here.
         */
        public RandomizedSet() {
            index = new HashMap<>();
            array = new ArrayList<>();
        }
    
        /**
         * Inserts a value to the set. Returns true if the set did not already contain the specified element.
         */
        public boolean insert(int val) {
            if (index.containsKey(val))
                return false;
            else {
                index.put(val, array.size());
                array.add(val);
                return true;
            }
        }
    
        /**
         * Removes a value from the set. Returns true if the set contained the specified element.
         */
        public boolean remove(int val) {
            if (!index.containsKey(val))
                return false;
            int indexVal = index.get(val);
            int valEnd = array.get(array.size() - 1);
            array.set(indexVal, valEnd);
            index.put(valEnd, indexVal);
            array.remove(array.size() - 1);
            index.remove(val);
            return true;
        }
    
        /**
         * Get a random element from the set.
         */
        public int getRandom() {
            return array.get(rand.nextInt(array.size()));
        }
    }
    
  • 相关阅读:
    分布式版本控制系统Git的安装与使用
    利用GitLab自动同步软件仓库
    MakerDAO 代币解释:DAI, WETH, PETH, SIN, MKR(一)
    数组 Major^
    String 类 Major^
    深度优先排序(数字全排列) Major^
    喊数字小游戏 Major^
    java数据类型 Major^
    ArrayList类的使用方法 Major^
    深度优先搜索(迷宫救人最短路径) Major^
  • 原文地址:https://www.cnblogs.com/xym4869/p/13763665.html
Copyright © 2020-2023  润新知