数组题目总结 -- 随机数问题

news2024/11/24 21:07:20

目录

  • 一. O(1) 时间插入、删除和获取随机元素
    • 思路和代码:
      • I. 博主的做法
      • II. 东哥的做法
      • III. 其他做法
  • 二. 黑名单中的随机数
    • 思路和代码:
      • I. 博主的做法
      • II. 东哥的做法

一. O(1) 时间插入、删除和获取随机元素

  • 题目链接:https://leetcode.cn/problems/insert-delete-getrandom-o1/

思路和代码:

I. 博主的做法

  • 博主只会写个输入输出,,,,等概率随机这个,不会。。

II. 东哥的做法

  • 这个问题可以分解一下:

    • 加入元素,时间复杂度O(1)
    • 删除元素,时间复杂度O(1)
    • 随机返回集合里的元素,使得每一个元素返回的可能性相同,时间复杂度也为O(1)
  • 也就是说存数据时间复杂度O(1),删除数据时间复杂度O(1),拿出任意元素时间复杂度也为O(1)

    • 拿出任意元素,复杂度为O(1),那么数据结构只可能是数组
    • 加入元素,就从数据末尾加入,时间复杂度也为O(1),这都好说,关键是删除元素,怎么能保证时间复杂度为O(1)呢?
    • 数组删除元素,时间复杂度为O(1)的情况,只能是删除末尾元素。那既然如此,那我们每次删除的时候,将要删除的元素和末尾元素交换,再删除,就OK了。当然交换的时间复杂度也是O(1),因为数组是用下标进行交换的。
    • 再想,插入,和删除的时候要判断数组中存在不存在这个元素,而数组检验这个操作,时间复杂度为O(n),不行,但哈希表可以!,我们用哈希表来存每一个元素的下标。
    • 当然在本题中,哈希表还将用来提取一个元素的下标,时间复杂度也是O(1)
  • 底层用数组作为数据结构,用哈希表存储每一个元素的下标。

class RandomizedSet {
    private List<Integer> arrayList;
    private Map<Integer, Integer> map; 

    public RandomizedSet() {
        arrayList = new ArrayList<>();
        map = new HashMap<>();
    }
    
    public boolean insert(int val) {
    	if(valToIndex.containsKey(val)) 
        //不能用if(arrayList.contains(val))
            return false;
        else{
            map.put(val, arrayList.size());
            arrayList.add(val);

            return true;
        }
    }
    
    public boolean remove(int val) {
    	if(!valToIndex.containsKey(val)) 
        //不能用if(!arrayList.contains(val))
            return false;
        else{
            int index = map.get(val);
            //下面两句顺序不能换
            map.put(arrayList.get(arrayList.size()-1), index);

            Collections.swap(arrayList, index, arrayList.size()-1);

            // arrayList.remove(arrayList.size()-1);
            //不能用arrayList.remove(val);
            arrayList.remove((Integer)val);
            map.remove(val);

            return true;
        }
    }
    
    public int getRandom() {
        //不能用 return arrayList.get((int)Math.random() * arrayList.size());
        return arrayList.get((int)(Math.random() * arrayList.size()));
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */
  • ArrayList有两种remove()的方法,如下图:
    在这里插入图片描述
    • 但在本题到当中,不能用arrayList.remove(val); 因为这样会默认val是下标。而将它转换成Integer对象:arrayList.remove((Integer)val);,那么调用的就是图中第二个方法了。当然这里也可以用下标进行删除:arrayList.remove(arrayList.size()-1);
  • 博主一开始想将下面两句换位置,结果报错,因为 如果先执行交换函数,那么arrayList当中的元素就已经交换了,而此时在进行对末尾元素的下标更新,那么现在ArrayList的末尾元素其实是原来index对应的元素。(相当于原来下标为index的元素,现在又更新到了index,发生错误)
    • 这里我们需要将arrayList的末尾元素下标更新为index,而val不需要更新,因为,马上就要删除val这个映射。
   map.put(arrayList.get(arrayList.size()-1), index);

   Collections.swap(arrayList, index, arrayList.size()-1);
  • return arrayList.get((int)(Math.random() * arrayList.size()));括号一定要加对位置,如果对Math.random()进行强转,那么结果只可能是0,也就是:return arrayList.get((int)Math.random() * arrayList.size());
    • Math.random()函数生成的是0 ~ 1之间的随机小数,再乘以arrayList的大小,就会生成0 ~ arrayList.size()之间的随机小数。我们此时对这个数强转int,结果作为下标,用来随机抽取元素。
  • 此时在执行 map.containsKey(val); 时,Java 会使用哈希函数将 val 映射到哈希表中的一个位置上,然后查找键为 val 的元素是否存在于哈希表中。
  • 哈希表的查找操作时间复杂度为 O(1),在某些特殊情况下,可能会导致哈希函数的冲突,从而使得哈希表的查找操作时间复杂度变高,甚至退化到 O(n)。
  • 时间复杂度:O(1)
  • 空间复杂度:O(n)

III. 其他做法

  • 主要区别体现在remove方法上,就是让arrayList末尾的元素替换val(也就是index对应的元素),此时,再删除末尾元素,就相当于删除了val。
 public boolean remove(int val) {
        if (!indices.containsKey(val)) {
            return false;
        }
        int index = indices.get(val);
        int last = nums.get(nums.size() - 1);
        //将last元素替换index对应的元素(val)
        nums.set(index, last);
        indices.put(last, index);
        nums.remove(nums.size() - 1);
        indices.remove(val);
        return true;
    }
  • 需要注意的是,arrayList.get()是下标 -> 元素;而map.get()是元素 -> 下标。

二. 黑名单中的随机数

  • 题目链接:https://leetcode.cn/problems/random-pick-with-blacklist/

思路和代码:

I. 博主的做法

  • 先创建一个0 - n的动态数组,再遍历blacklist数组,如果动态数组中有这个数,那么将它删掉。
  • 代码没有问题,但是超出内存限制,无语。。
class Solution {
    private List<Integer> list;

    public Solution(int n, int[] blacklist) {
        list = new ArrayList<>();
        for(int i = 0; i < n; i++)
            list.add(i);
        
        for(int num : blacklist){
            if(list.contains(num))
                list.remove((Integer)num);
        }
    }
    
    public int pick() {
        return list.get((int)(Math.random() * list.size()));
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(n, blacklist);
 * int param_1 = obj.pick();
 */

II. 东哥的做法

  • 类似于上一个做法,将数组变成[0,size)为非黑名单数,[size,n)为黑名单数,这样我们就可以在[0,size)上取随机数进行提取了。

    • 创建一个hashmap,加入所有黑名单数。
    • 如果黑名单数本来就在size之后,那么就不需要再交换;
    • 如果在size之前,
      • 用last当做指针,从后往前,找到在size之后的非黑名单数字的索引。
      • 将size前黑名单的索引,替换成size之后非黑名单数字的索引。
        在这里插入图片描述
        ※ eg:这个图,4 显然在size的右边,不需要映射,跳过就好。此处需要的是将 1 的索引映射为 3 的索引。
  • pick()方法时,如果随机抽取,命中黑名单数,那么,返回map当中映射的非黑名单数的索引;如果是正常数字,那么返回它自己的索引就可以。
    在这里插入图片描述※ 最终的结果就是上面这个图,0,1为size前黑名单数,将它的索引映射到size之后的非黑名单数3,4的索引。然后,我们直接再[0,size)上随机进行取值就可以了。

class Solution {
    private int size;
    private Map<Integer, Integer> map;

    public Solution(int n, int[] blacklist) {
        map = new HashMap<>();
        size = n - blacklist.length;
		//将黑名单数组存入map,后面什么数字都行,目的是将黑名单数字加入map当中
        for(int i : blacklist)
            map.put(i, -1);
        int last = n - 1;
		//索引交换
        for(int i : blacklist){
            if(i >= size)
                continue;
            //找到一个size之后的非黑名单数字
            while(map.containsKey(last))
                last--;
            //将size前黑名单数字映射到size后非黑名单数字上(相当于两个数字进行了交换)
            map.put(i, last);
            last--;
        }
    }
    
    public int pick() {
        int index = (int)(Math.random() * size);
        //如果存在,get(index),否则,返回index
        return map.getOrDefault(index, index);
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(n, blacklist);
 * int param_1 = obj.pick();
 */
  • 当时博主一直没有理解if(i >= size) 这个啥意思,其实可以带个值,例如第一个图当中,N = 5,blacklist.length = 2,那么最后我们随机提取数组的长度就是N - blacklist.length = 3。因为原来默认的数组是[ 0,N - 1 ],也就是递增的,所以,只要是 >=size的黑名单数就不用管了。
    在这里插入图片描述
  • 时间复杂度:O(m),m 是 blacklist 的长度。初始化hashmap为O(m),替换的时候,while遍历,最坏的结果就是 blacklist 全部都在 size 之前,那么那个last- -;这条语句,执行完整个循环,走了m,所以为O(m)。(size之后的每个数字要么是黑名单数,要么被一个黑名单数所映射,因此while循环增加了 m 次)
  • 空间复杂度:O(m),构建哈希表要 m 的空间。

参考:
https://leetcode.cn/problems/random-pick-with-blacklist/solution/hei-ming-dan-zhong-de-sui-ji-shu-by-leet-cyrx/
https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-48c1d/chang-shu–6b296/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/616760.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何提升邮件点击率?诀窍速递

相比邮件的打开率&#xff0c;邮件点击率可以更好地评估邮件营销的表现&#xff0c;帮助营销人员评估营销活动是否有帮助推动销售、培养潜在客户或提升品牌知名度等。如何更好地提升电子邮件的点击率&#xff0c;本文6个小贴士来助力。 这里给大家推荐一站式客服系统SaleSmartl…

芒种忙什么呢?当然是抢个中国人民大学与加拿大女王大学金融硕士项目的入学名额了

今日是农历的芒种&#xff0c;大家都在忙什么呢&#xff1f;“芒种”一词最早出自《周礼》中的“泽草所生&#xff0c;种之芒种。”其中“芒”指的是在阳光照耀下如波浪般摇摆的麦芒&#xff0c;表明此时已经成熟可以收割了。而“种”则代表稻谷&#xff0c;这时候是种稻谷的最…

066:cesium flyto一个具体的点位置

第066个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置飞行定位功能,如果你知道位置的经纬度和高度,你可以使用相机的 flyTo 函数直接飞到 CesiumJS 中的那个位置。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式…

【数据存储:揭开内存中数据存储的神秘面纱】

本节重点 -- 重中之重 数据类型详细介绍 整形在内存中的存储&#xff1a;原码、反码、补码 大小端字节序介绍及判断 浮点型在内存中的存储解析 准备好了&#xff0c;开始啰&#xff0c;在小小的花园里面......最近被这个歌曲洗脑&#xff0c;但是我们并不是要唱歌&#xff0c;而…

分布式数据库HBase

大数据基础-分布式数据库HBase 概述HBase简介HBase与传统关系数据库的对比分析 HBase数据模型数据模型概述数据模型相关概念数据坐标 HBase的实现原理HBase功能组件表和RegionRegion的定位HBase框架结构HMasterRegionServerHBase协作组件RegionColumnFamilyKeyValue小结 HBase运…

Vue基础第四篇

一、计算属性 计算属性是基于它们的依赖进行缓存的 计算属性只有在它的相关依赖发生改变时才会重新求值 计算属性就像Python中的property&#xff0c;可以把方法/函数伪装成属性 1.通过计算属性实现名字首字母大写 <!DOCTYPE html> <html lang"en">…

k8s入门(三)之StatefulSet、Service、Ingress

一、StatefulSet操作 – 1、初体验 可以发现等待前一个启动好了&#xff0c;在启动下一个&#xff0c;因此容器的健康检查失败也会影响启动 域名&#xff1a;web-2.nginx.default.svc.cluster.local,一般写到web-2.nginx.default就可以了 [rootmaster01 ~]# cat ssweb.yaml a…

2023 年 10 个最佳 Java IDE 和编辑器

集成开发环境或 IDE 是构建软件项目所需的最关键部分之一。它们使开发变得简单高效。 IDE 通过缩短设置时间、加速开发任务、让开发人员了解最新的危险和最佳实践以及标准化开发过程以便任何人都可以参与来提高开发人员的生产力。 IDE 还可以帮助重构开发过程。它们使程序员能…

HTTP请求报文解剖

HTTP请求报文解剖 HTTP请求报文由3部分组成&#xff08;请求行请求头请求体&#xff09;&#xff1a; 下面是一个实际的请求报文&#xff1a; ①是请求方法&#xff0c;GET和POST是最常见的HTTP方法&#xff0c;除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。不过&#…

Thread 类基本用法详解

Thread 类基本用法详解 Thread类的作用线程创建继承 Thread, 重写 run实现 Runnable, 重写 run继承 Thread, 重写 run, 使用匿名内部类实现 Runnable, 重写 run, 使用匿名内部类使用 lambda 表达式(最推荐) 线程中断1.使用标志位来控制线程是否要停止2.使用Thread自带的标志位来…

软件测试商城项目----优惠券测试流程

优惠券冒烟测试 后台添加优惠券&#xff0c;前端领取&#xff0c;消费并成功扣除对应金额&#xff0c;如果冒烟测试不通过不进行下面的测试。 优惠券基本信息测试 &#xff08;1&#xff09;优惠券名称 &#xff08;2&#xff09;优惠券副标题 &#xff08;3&#xff09;优惠券…

ubuntu2204server虚拟机创建

ubuntu2204 虚拟机创建系统安装系统配置 虚拟机创建ubuntu2204&#xff0c;然后配置清华源&#xff0c;修改网卡已经ssh文件&#xff0c;实现远程连接 虚拟机创建 一路回车默认到创建磁盘 一路回车到自定义硬件 添加一块网盘&#xff0c;其他的是否添加自己根据实际情况&…

【C刷题笔记】找单身狗问题

目录 版本1:在数组内只有一个元素没有成对出现 版本2:在数组内有两个元素没有成对出现 第一步:异或所有元素,异或就是相同为0&#xff0c;相异为1 第二步:计算ret的二进制中哪一位元素是1 第三步:开始分组异或 1.分组: 2.异或 版本1:在数组内只有一个元素没有成对出现 单…

基于C/C++将派生类赋值给基类的详细讲解

目录 将派生类对象赋值给基类对象 将派生类指针赋值给基类指针 将派生类引用赋值给基类引用 本期推荐书目 在 C/C中经常会发生数据类型的转换&#xff0c;例如将 int 类型的数据赋值给 float 类型的变量时&#xff0c;编译器会先把 int 类型的数据转换为 float 类型再赋值&a…

华为OD机试 Java 实现【查找兄弟单词】【牛客练习题 HJ27】,附详细解题思路

一、题目描述 定义一个单词的“兄弟单词”为&#xff1a;交换该单词字母顺序&#xff08;注&#xff1a;可以交换任意次&#xff09;&#xff0c;而不添加、删除、修改原有的字母就能生成的单词。 兄弟单词要求和原来的单词不同。例如&#xff1a; ab 和 ba 是兄弟单词。 ab …

三防工业平板电脑搭载大容量聚合物锂电池,满足全天候移动式作业

在制造业、物流业等行业中&#xff0c;电脑经常会接触到水&#xff0c;因此防水是非常重要的。工业三防平板电脑采用了特殊的防水技术&#xff0c;能够在水中工作&#xff0c;即使在雨天或水花溅到电脑上时也不会受到损坏。 三防工业平板电脑采用8核ARM架构处理器&#xff0c;2…

QML开发避坑指南(四):ImageProvider的使用

&#xff08;1&#xff09;引子 首先&#xff0c;ImageProvider是为QML应用提供的、一种先进的图像加载方式。 作者建议从三点理解其定义&#xff1a; 加载方式的差异&#xff1a; 相比本地资源的加载方式&#xff1a;source : “qrc:///前缀/demo.jpg”。 ImageProvider方式…

使用 ChatGPT API 构建系统(三):思维链推理

今天我学习了DeepLearning.AI的 Building Systems with the ChatGPT API 的在线课程&#xff0c;我想和大家一起分享一下该门课程的一些主要内容。 下面是我们通过Open API来访问ChatGPT模型的主要代码&#xff1a; import openai#您的openai的api key openai.api_key YOUR-O…

使用FS_Hi3861鸿蒙开发板编译时常见的一些问题

本文档支持的是下面的产品 华清远见 FS-Hi3861https://gitee.com/HiSpark/hi3861_hdu_iot_application/blob/master/src/vendor/hqyj/fs_hi3861/doc/%E5%8D%8E%E6%B8%85%E8%BF%9C%E8%A7%81%20FS_Hi3861%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC.md 目录 在添加第三方库完成后&a…

想要精通算法和SQL的成长之路 - 下一个排列

想要精通算法和SQL的成长之路 - 下一个排列 前言一. 下一个排列二. 下一个更大元素III 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 下一个排列 原题链接 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如&#xff0c;arr [1,2,3] &#xff0c;以下…