【算法挑战】常数时间插入、删除和获取随机元素(含解析、源码)

news2024/11/28 0:55:58

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

https://leetcode-cn.com/problems/insert-delete-getrandom-o1/

题目描述

设计一个支持在平均 时间复杂度 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();

思路

首先得考虑的是,用数组还是用链表来存,先来复习一下数组和链表常见操作的时间复杂度吧。

数组操作时间复杂度
随机访问 O ( 1 ) O(1) O(1)
插入数值到数组 O ( N ) O(N) O(N)
插入数值到数组最后 O ( 1 ) O(1) O(1)
从数组删除数值 O ( N ) O(N) O(N)
从数组最后删除数值 O ( 1 ) O(1) O(1)
链表操作时间复杂度
访问 O ( N ) O(N) O(N)
插入数值到链表 O ( N ) O(N) O(N)
插入数值到链表开头 O ( 1 ) O(1) O(1)
从链表删除数值 O ( N ) O(N) O(N)
从链表开头删除数值 O ( 1 ) O(1) O(1)

很显然,链表时间复杂度为 O ( N ) O(N) O(N) 的访问操作并不符合我们的需求,所以我们还是选择数组来作为存储数据的容器。

插入

首先要实现常数时间插入元素,我们只能在数组最后插入。

在数组最后插入元素

Untitled-2020-06-05-2052

在数组其他位置插入元素

insert-o(n)

获取随机元素

这个就很简单了,因为数组是可以通过下标随机访问的,我们只需要生成一个 0 ~ N-1 的随机数即可,N 为数组长度。

删除

删除元素的操作可以分为两种:

  1. 删除末尾元素,时间复杂度为 O(1)

remove-last

  1. 删除非末尾元素,因为删除位置之后的每个元素都要向前移动一步,所以时间复杂度是 O(N)

remove-o(n)

显然,如果我们想实现题目要求的 O(1) 时间的删除,只能在数组末尾进行删除操作。具体做法就是把要删除的元素和末尾的元素换个位置,然后再从数组末尾删除。

remove-o(1)
那我们再来看看 API 是怎么用的:

  • set.insert(2) 表示往集合中插入数值 2,成功插入返回 true,如果 2 已经存在集合中返回 false
  • set.remove(2) 表示从集合中删除数值 2,成功删除返回 true,如果 2 不存在集合中返回 false

可以看到这两个方法的参数都是值,而在数组中,要在常数时间内找到一个元素,必须要知道它的下标。所以显然我们还需要一个结构来记录集合中的值和它所在的数组下标的关系,这样一系列 值->下标 的对应关系,你应该能想到用一个哈希表来记录。

hashmap 2

数组中存放着真正的值,而哈希表中存放着每个值所对应的数组下标。

但是,还有一个问题,还记得删除操作么?我们是先把要删除的元素和最后的元素换了位置再删除,换了位置后,两个元素的下标也变了。

remove-swap

所以很显然的,删除某个元素后,我们的哈希表也需要更新。

update-hashmap

代码

class RandomizedSet {
    constructor() {
        // store the actual values
        this.array = [];
        // store the value-> index mapping
        this.map = {};
    }

    insert(val) {
        if (val in this.map) return false;
        this.array.push(val);
        this.map[val] = this._size() - 1;
        return true;
    }

    remove(val) {
        if (!(val in this.map)) return false;

        const index = this.map[val];
        const lastIndex = this._size() - 1;
        if (index < lastIndex) {
            this._swap(index, lastIndex);
            this.map[this.array[index]] = index;
        }
        this.array.pop();
        delete this.map[val];
        return true;
    }

    getRandom() {
        const size = this._size();
        if (size === 0) return false;
        let randomIndex = Math.floor(Math.random() * size);
        return this.array[randomIndex];
    }

    _size() {
        return this.array.length;
    }

    _swap(a, b) {
        const temp = this.array[b];
        this.array[b] = this.array[a];
        this.array[a] = temp;
    }
}

Originally posted by @suukii in https://github.com/leetcode-pp/91alg-1/issues/23#issuecomment-640231502

[参考题解](Originally posted by @azl397985856 in https://github.com/leetcode-pp/91alg-1/issues/23#issuecomment-640155651)

总结

以上就是本文所有内容了,希望能对你有所帮助,能够解决常数时间插入、删除和获取随机元素问题。

如果你喜欢本文,也请务必点赞、收藏、评论、转发,这会对我有非常大的帮助。请我喝杯冰可乐也是极好的!

已完结,欢迎持续关注。下次见~

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

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

相关文章

【快速解决】Android Button页面跳转功能

目录 让我们直接开始 第一步&#xff1a;先建立一个新的activity ​编辑 第二步&#xff1a;打开第一个页面的Java文件MainActivity 方法一&#xff1a;直接跳转功能如下&#xff1a; 方法二&#xff1a;输入密码才能进行跳转功能如下&#xff1a; 需要注意的地方 结语 让…

音视频报警可视对讲15.6寸管理机

音视频报警可视对讲15.6寸管理机 一、管理机技术指标&#xff1a; 1、15.6寸原装京东方工业液晶触摸屏&#xff0c;分辨率1920 (H) x 1080 (V)&#xff1b; 2、1000M/100M自适应双网口&#xff1b; 4、按键设置&#xff1a;报警/呼叫按键&#xff0c;通话/挂机按键&#xff…

RabbitMQ 消息应答与发布

目录 一、消息应答 1、自动应答&#xff08;默认&#xff09; 2、手动消息应答的方法 ​编辑 3、消息重新入队 4、手动应答案列与效果演示 二、RabbitMQ持久化 1、队列持久化 2、消息持久化 三、不公平分发&#xff08;能者多劳&#xff0c;弱者少劳&#xff09; 1、…

人工智能师求职面试笔试题及答案汇总

人工智能师求职面试笔试题及答案汇总 1.如何在Python中实现一个生成器&#xff1f; 答&#xff1a;在Python中&#xff0c;生成器是一种特殊类型的迭代器。生成器允许你在需要时才生成值&#xff0c;从而节省内存。生成器函数在Python中是通过关键字yield来实现的。例如&…

Linux 安装node并全局可用

前言 基于&#xff1a;操作系统 CentOS 7.6 工具&#xff1a;Xshell7、Xftp7 1.下载 根目录创建一个 node 文件夹并进入 mkdir /node && cd /node下载压缩包 wget https://nodejs.org/download/release/v16.18.0/node-v16.18.0-linux-x64.tar.gz2.解压并重命名 …

AI:53-基于机器学习的字母识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…

字节测试开发面试分享,太难了...

一面 首先&#xff0c;自我介绍&#xff0c;我介绍了自己的技术栈和项目。 技术栈提到过Spring、Redis、Kafka、Docker、K8s、大数据。 项目提到过接口和UI自动化。 我有个大数据平台项目&#xff0c;问了比较多&#xff0c;聊着聊着&#xff0c;提到自己研究过Selenium、T…

MongoDB设置密码

关于为什么要设置密码 公司的测试服务器MongoDB服务对外网开放的&#xff0c;结果这几天发现数据库被每天晚上被人清空的了&#xff0c;还新建了个数据库&#xff0c;说是要支付比特币。查了日志看到有个境外的IP登录且删除了所有的集合。所以为了安全起见&#xff0c;我们给m…

centos9 stream 下 rabbitmq高可用集群搭建及使用

RabbitMQ是一种常用的消息队列系统&#xff0c;可以快速搭建一个高可用的集群环境&#xff0c;以提高系统的弹性和可靠性。下面是搭建RabbitMQ集群的步骤&#xff1a; 基于centos9 stream系统 1. 安装Erlang和RabbitMQ 首先需要在所有节点上安装Erlang和RabbitMQ。建议使用官…

【TypeScript】认识TypeScript

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录 TypeScriptJavascript的缺点使用TypeScript重构TypeScript的编译环境全局安装TS编译环境TS编译简…

视频会议系统方案报价

视频会议系统 报价方案是咨询视频会议系统价格用户所关注的&#xff0c;但是报价是一个比较细致的工作&#xff0c;需要从多维度进行对比。 1. 视频会议终端设备费用&#xff1a;根据所需设备的数量和所选设备价格确定。视频会议终端类型各异&#xff0c;摄像头、麦克风、显示设…

文件上传漏洞实战getshell

目录 0x01 信息收集 0x02 寻找接口 0x03 拼接路径 0x04 权限 0x01 信息收集 通过fofa&#xff0c;子域名收集等相关工具搜索域名 定位到站点&#xff1a;htps://xx..edu.cn/x/xx/ 0x02 寻找接口 通过f12寻找相关的js&#xff0c;发现有其他的页面 0x03 拼接路径 https://xx…

【C语法学习】16 - fclose()函数

文章目录 1 函数原型2 参数3 返回值4 示例 1 函数原型 fclose()&#xff1a;关闭已打开的文件&#xff0c;并刷新缓冲区&#xff0c;函数原型如下&#xff1a; int fclose(FILE *stream);2 参数 fclose()函数只有一个参数stream&#xff1a; 参数stream是一个指向FILE类型结…

curl(三)传递数据

一 基础铺垫 ① form表单回顾 关注&#xff1a; from表单涉及method、content-type enctype和Content-type有什么关系 ② Content-Type 思考&#xff1a;数据传输格式和解析类型不一致导致哪些特性? ③ application/x-www-form-urlencoded 1、GET方式 2、POST方式 ④ …

佳易王电玩手柄游戏厅倒计时语音提醒软件试用下载

佳易王电玩手柄游戏厅倒计时语音提醒软件试用下载 一、佳易王电玩PS5游戏厅计时计费软件部分功能简介&#xff1a; 1、计时计费功能 &#xff1a;开台时间和所用的时长直观显示&#xff0c;每3秒即可刷新一次时间。 2、销售商品功能 &#xff1a;商品可以绑定桌子最后一起结…

VUE2和VUE3思维导图知识体系总结大对比

VUE2知识体系 VUE3知识体系 思维导图原件下载地址

3.22每日一题(二重积分求平面区域面积)

先复习求平面积分的公式 注&#xff1a;面对平面积分直接使用二重积分对1求积分即可&#xff1b;所以只需要背二重积分的两个公式&#xff1a; 1、直角坐标下对1积分 2、极坐标下对1积分 xy-1是等轴双曲线&#xff01;&#xff01; 1、先画图定区域 2、选择先对x积分还是先对…

APM建设踩了哪些坑?去哪儿旅行分布式链路追踪系统实践

一分钟精华速览 分布式链路追踪系统在企业的APM体系中扮演着重要的角色。本文分享了去哪儿旅行构建分布式链路追踪系统的实践经验。从APM整体架构设计入手&#xff0c;讲述了日志收集、Kafka传输和Flink任务处理等环节的性能优化实践和踩坑经验。 同时&#xff0c;作者结合丰…

4 sql语法基础

1、DISTINCT 相同值只会出现一次。它作用于所有列&#xff0c;也就是说所有列的值都相同才算相同。 2、LIMIT 限制返回的行数。可以有两个参数&#xff0c;第一个参数为起始行&#xff0c;从 0 开始&#xff1b;第二个参数为返回的总行数。 返回前 5 行: SELECT * FROM myt…

FFmpeg直播能力更新计划与新版本发布

// 编者按&#xff1a;客户端作为直接面向用户大众的接口&#xff0c;随着技术的发展进化与时俱进&#xff0c;实现更好的服务是十分必要的。FFmpeg作为最受欢迎的视频和图像处理开源软件&#xff0c;被相关行业的大量用户青睐&#xff0c;而随着HEVC标准的发布到广泛使用&am…