• Leetcode 380. 常数时间插入、删除和获取随机元素(中等) 710. 黑名单中的随机数(困难) 使用数组实现随机查找以及等概率随机返回


    labuladong讲解

    380. 常数时间插入、删除和获取随机元素(中等)

    题目:

     题目理解:

    就是说就是让我们实现如下一个类:

    class RandomizedSet {
    public:
        /** 如果 val 不存在集合中,则插入并返回 true,否则直接返回 false */
         bool insert(int val) {}
        
        /** 如果 val 在集合中,则删除并返回 true,否则直接返回 false */
        bool remove(int val) {}
        
        /** 从集合中等概率地随机获得一个元素 */
        int getRandom() {}
    }

    思路:

    底层用数组实现,且数组必须是紧凑的。

    这样我们就可以直接生成随机数作为索引,从数组中取出该随机索引对应的元素,作为随机元素。

    如果我们想在 O(1) 的时间删除数组中的某一个元素 val,可以先把这个元素交换到数组的尾部,然后再 pop 掉。

    交换两个元素必须通过索引进行交换对吧,那么我们需要一个哈希表 valToIndex 来记录每个元素值对应的索引。

    class RandomizedSet {
    public:
        RandomizedSet() {
    
        }
        
        bool insert(int val) {
            // 若 val 已存在,不用再插入
            if(m.count(val)>0){
                return false;
            }
            // 若 val 不存在,插入到 nums 尾部,
            // 并记录 val 对应的索引值
            m[val]=num.size();
            num.push_back(val);
            return true;
        }
        
        bool remove(int val) {
            // 若 val 不存在,不用再删除
            if(m.count(val)==0){
                return false;
            }
            // 先拿到 val 的索引
            int index=m[val];
            // 将最后一个元素对应的索引修改为 index
            m[num.back()]=index;
            // 交换 val 和最后一个元素
            swap(num[index],num[num.size()-1]);
            // 在数组中删除元素 val
            num.pop_back();
            // 删除元素 val 对应的索引
            m.erase(val);
            return true;
        }
        
        int getRandom() {
            // 随机获取 nums 中的一个元素
            return num[rand()%num.size()];
        }
        // 存储元素的值
        vector<int> num;
        // 记录每个元素对应在 nums 中的索引
        unordered_map<int,int> m;
    };

    710. 黑名单中的随机数(困难)

    题目:

    给你输入一个正整数 N,代表左闭右开区间 [0,N),再给你输入一个数组 blacklist,其中包含一些「黑名单数字」,且 blacklist 中的数字都是区间 [0,N) 中的数字。

    现在要求你设计如下数据结构:

    class Solution {
    public:
        // 构造函数,输入参数
        Solution(int N, vector<int>& blacklist) {}
        
        // 在区间 [0,N) 中等概率随机选取一个元素并返回
        // 这个元素不能是 blacklist 中的元素
        int pick() {}
    };

    pick 函数会被多次调用,每次调用都要在区间 [0,N) 中「等概率随机」返回一个「不在 blacklist 中」的整数。

    这应该不难理解吧,比如给你输入 N = 5, blacklist = [1,3],那么多次调用 pick 函数,会等概率随机返回 0, 2, 4 中的某一个数字。

    而且题目要求,在 pick 函数中应该尽可能少调用随机数生成函数 rand()

    思路:

    我们可以将区间 [0,N) 看做一个数组,然后将 blacklist 中的元素移到数组的最末尾,同时用一个哈希表进行映射

    相当于把黑名单中的数字都交换到了区间 [sz, N) 中,同时把 [0, sz) 中的黑名单数字映射到了正常数字。

    映射时需要注意,如果黑名单数组本身就在[sz,N)中,则可以直接跳过,不用保存到哈希表中。同时,映射数字也需要跳过。

    class Solution {
    public:
        Solution(int n, vector<int>& blacklist) {
            // 先将所有黑名单数字加入 map
            for(int i=0;i<blacklist.size();++i){
                // 这里赋值多少都可以
                // 目的仅仅是把键存进哈希表
                // 方便快速判断数字是否在黑名单内
                m[blacklist[i]]=666;
            }
            int last=n-1;
            sz=n-blacklist.size();
            for(int i=0;i<blacklist.size();++i){
                // 如果 b 已经在区间 [sz, N)
                // 可以直接忽略
                if(blacklist[i]>=sz){
                    continue;
                }
                // 跳过所有黑名单中的数字
                while(m.count(last)>0){
                    last--;
                }
                // 将黑名单中的索引映射到合法数字
                m[blacklist[i]]=last;
                last--;
            }
        }
        
        int pick() {
            // 随机选取一个索引
            int index=rand()%sz;
            // 这个索引命中了黑名单,
            // 需要被映射到其他位置
            if(m.count(index)>0)
                return m[index];
            // 若没命中黑名单,则直接返回
            return index;
        }
        unordered_map<int,int> m;
        int sz;
    };
  • 相关阅读:
    数组指针和指针数组
    C#反射机制
    浅探委托(delegate)和事件(event)
    C#的is和as操作符来进行强制类型转换&&值类型的拆箱、装箱
    2018-2-8
    JSP--语法
    JSP中的<%%>,<%! %>,<%= %>,<%-- --%>
    JSP--简介
    springmvc实现文件下载到Android手机设备pda端
    常用的正则表达式(转)
  • 原文地址:https://www.cnblogs.com/zl1991/p/15945698.html
Copyright © 2020-2023  润新知