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

一、题目分析

在这里插入图片描述

(一)功能需求

我们需要实现 RandomizedSet 类,包含以下功能:

  • RandomizedSet():初始化数据结构。
  • bool insert(int val):当元素 val 不存在时,插入该元素并返回 true;若已存在,返回 false 。
  • bool remove(int val):当元素 val 存在时,移除该元素并返回 true;若不存在,返回 false 。
  • int getRandom():随机返回现有集合中的一个元素,每个元素被返回的概率相同,且调用时集合至少有一个元素 。

(二)核心挑战

要让插入、删除、获取随机元素操作的平均时间复杂度都达到 O(1) 。常规的数据结构往往难以单独满足这些要求,比如哈希表虽能高效插入和删除,但难以高效随机获取;动态数组便于随机获取,但插入和删除(尤其是中间元素)的时间复杂度较高。所以需要结合两者的优势来实现。

二、算法思想:哈希表 + 动态数组的协同运用

(一)哈希表(Map)的作用

使用 Map<Integer, Integer> 来存储元素 val 以及它在动态数组中的索引。这样可以:

  • 插入元素时,快速判断元素是否已存在(通过 map.containsKey(val) ),时间复杂度为 O(1) 。
  • 删除元素时,快速获取元素在动态数组中的位置,为后续在动态数组中高效删除元素做准备,时间复杂度为 O(1) 。

(二)动态数组(List)的作用

使用 List 来存储元素的值,方便进行随机获取操作。因为要实现随机获取元素且每个元素概率相同,我们可以利用 Random 类生成随机索引(范围是 0 到 list.size() - 1 ),然后通过 list.get(randomIndex) 快速获取元素,时间复杂度为 O(1) 。

(三)删除操作的优化

删除元素时,直接删除动态数组中间的元素时间复杂度会是 O(n) (需要移动元素 )。为了优化这个操作,我们采用“交换删除”的策略:

  1. 获取要删除元素 val 在动态数组中的索引 reNumIndex ,以及动态数组最后一个元素 lastNum 。
  2. 将动态数组中 reNumIndex 位置的元素替换为 lastNum 。
  3. 更新 lastNum 在哈希表中的索引为 reNumIndex 。
  4. 删除动态数组的最后一个元素(时间复杂度 O(1) ),并从哈希表中移除 val 。这样就将删除操作的时间复杂度优化到了 O(1) 。

三、代码实现与详细注释

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;class RandomizedSet {// 哈希表,键为元素值,值为该元素在 numsList 中的索引Map<Integer, Integer> map; // 动态数组,存储元素的值,用于随机获取List<Integer> numsList; // 用于生成随机索引Random random; // 构造函数,初始化数据结构public RandomizedSet() {map = new HashMap<>();numsList = new ArrayList<>();random = new Random();}// 插入元素操作public boolean insert(int val) {// 判断元素是否已存在于哈希表中if (map.containsKey(val)) { return false; // 已存在,返回 false} else {// 将元素 val 与它在 numsList 中的索引(当前 numsList 的长度,因为即将添加到末尾)存入哈希表map.put(val, numsList.size()); // 将元素添加到 numsList 末尾numsList.add(val); return true; // 插入成功,返回 true}}// 删除元素操作public boolean remove(int val) {// 判断元素是否存在于哈希表中if (map.containsKey(val)) { // 获取动态数组最后一个元素的值int lastNum = numsList.get(numsList.size() - 1); // 获取要删除元素 val 在 numsList 中的索引int reNumIndex = map.get(val); // 将 numsList 中 reNumIndex 位置的元素替换为 lastNumnumsList.set(reNumIndex, lastNum); // 更新 lastNum 在哈希表中的索引为 reNumIndexmap.put(lastNum, reNumIndex); // 删除 numsList 的最后一个元素(时间复杂度 O(1) )numsList.remove(numsList.size() - 1); // 从哈希表中移除要删除的元素 valmap.remove(val); return true; // 删除成功,返回 true} else {return false; // 元素不存在,返回 false}}// 随机获取元素操作public int getRandom() {// 生成一个 0 到 numsList.size() - 1 范围内的随机索引int randomIndex = random.nextInt(numsList.size()); // 根据随机索引获取元素并返回return numsList.get(randomIndex); }
}/*** 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();*/

四、复杂度分析

(一)时间复杂度

  • 插入操作(insert):主要操作是哈希表的 containsKey 和 put ,以及动态数组的 add 操作。哈希表的这两个操作时间复杂度是 O(1) ,动态数组 add 操作(在末尾添加)时间复杂度也是 O(1) ,所以插入操作平均时间复杂度为 O(1) 。
  • 删除操作(remove):通过“交换删除”的优化,哈希表的 containsKey、get、put、remove 操作,以及动态数组的 get、set、remove(末尾删除 )操作,时间复杂度都是 O(1) ,所以删除操作平均时间复杂度为 O(1) 。
  • 随机获取操作(getRandom):生成随机索引和动态数组的 get 操作时间复杂度都是 O(1) ,所以该操作平均时间复杂度为 O(1) 。

(二)空间复杂度

哈希表存储了 n 个元素(n 是集合中元素的数量 ),空间复杂度为 O(n) ;动态数组也存储了 n 个元素,空间复杂度为 O(n) ;Random 类占用常数空间。所以总的空间复杂度为 O(n) ,n 是集合中元素的最大数量。

O(1) 时间插入、删除和获取随机元素这道题,通过巧妙结合哈希表和动态数组,充分发挥了两者的优势,成功实现了插入、删除、随机获取操作平均时间复杂度为 O(1) 的需求。其中删除操作的“交换删除”策略,更是优化时间复杂度的关键。

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

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

相关文章

前端开发的面试自我介绍与准备

前端面试自我介绍不知道怎么说的&#xff0c;直接参考下面的模板&#xff0c;然后换成你的经历 自我介绍控制在1分钟左右&#xff0c;千万不要说的太久&#xff0c;面试官会烦的&#xff0c;但是又不好意思打断你 切记面试是人和人面对面的交流&#xff0c;要有&#xff0c;面试…

10、系统规划与分析

一、系统规划步骤系统规划步骤对现有系统进行初步调查分析和确定系统目标分析子系统的组成和基本功能拟定系统的实施方案拟定系统的可行性研究指定系统建设方案系统规划阶段的产出物&#xff1a;可行性研究报告、系统设计任务书。习题1、拟定系统的实施方案是在系统规划阶段完成…

Nginx学习笔记(六)—— Nginx反向代理

&#x1f4da;Nginx学习笔记&#xff08;六&#xff09;—— Nginx反向代理 &#x1f4cc; 一、反向代理核心概念 本质原理&#xff1a; #mermaid-svg-UkFRDp2Ut7MK5T2N {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-s…

三伍微电子GSR2406 IoT FEM 2.4G PA 射频前端模组芯片

三伍微电子GSR2406 IoT FEM 2.4G PA 射频前端模组芯片规格书Product Description The GSR2406 is a high-performance, fully integrated RF front-end module (FEM) designed for Zigbee technology, Thread, and Bluetooth (including low energy) applications. The GSR2406…

开发避坑指南(24):RocketMQ磁盘空间告急异常处理,CODE 14 “service not available“解决方案

异常信息 Caused by: org.apache.rocketmq.client.exception.MQBrokerException: CODE: 14 DESC: service not available now, maybe disk full, CL: 0.94 CQ: 0.94 INDEX: 0.94, maybe your broker machine memory too small.异常背景 一个项目里面用到了rocketmq&#x…

开源WAF新标杆:雷池SafeLine用语义分析重构网站安全边界

文章目录前言【视频教程】1.安装Docker2.本地部署SafeLine3.使用SafeLine4.cpolar内网穿透工具安装5.创建远程连接公网地址6.固定Uptime Kuma公网地址前言 当个人或企业站点上线后面临的首要威胁往往来自网络攻击——据统计&#xff0c;超过60%的Web应用漏洞利用尝试在流量到达…

Mac M1探索AnythingLLM+SearXNG

SearXNG 能聚合来自多达 200 多个搜索服务&#xff0c;可私有化部署&#xff0c;并提供了灵活自定义选项。 AnythingLLMSearXNG&#xff0c;刚好能解决AnythingLLM因为网络限制导致web search不可用的问题。 1 安装docker 下载mac m1版本的docker并安装。 https://docs.dock…

模式设计:策略模式及其应用场景

简介 策略模式(Strategy Pattern)是一种行为型设计模式,它允许在运行时动态选择算法或行为。核心思想是将算法封装成独立的类(策略),使它们可以相互替换,让算法的变化独立于使用它的客户端。 核心思想 解耦:将算法的定义与使用分离。每个算法封装起来,使它们可以互…

Squash Merge(压缩合并)和Rebase Merge(变基合并)介绍

文章目录**1. Squash Merge&#xff08;压缩合并&#xff09;****定义****操作步骤****特点****优点****缺点****2. Rebase Merge&#xff08;变基合并&#xff09;****定义****操作步骤****特点****优点****缺点****3. 对比总结****4. 选择建议****5. 示例场景****Squash Merg…

Linux编程 —— framebuffer

一、framebuffer概念framebuffer&#xff1a;帧缓冲&#xff0c;帧缓存技术Linux内核专门为图形化显示提供的一套应用程序接口。二、基本操作步骤1. 打开显示设备(/dev/fb0) 2. 获取显示设备相关参数&#xff08;分辨率&#xff0c;像素格式&#xff09;---》ioctl 3. 建立显存…

文件编辑html

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文件行内容编辑器</title><script src&…

具有熔断能力和活性探测的服务负载均衡解决方案

一、整体架构设计 1.核心组件 负载均衡器&#xff1a;负责选择可用的服务节点健康检查器&#xff1a;定期检测服务节点的可用性服务节点管理&#xff1a;维护所有可用节点的状态信息 2.负载均衡策略 轮询(Round Robin)随机(Random)加权轮询(Weighted Round Robin)最少连接(Leas…

技术演进中的开发沉思-62 DELPHI VCL系列:VCL下的设计模式

今天聊聊设计模式&#xff0c;当然这个章节目前仅限于DELPHI VCL,因为接下来梳理的Factory/Factory Method、Bootstrap 和 ForEach 这三种设计样例&#xff0c;看似独立&#xff0c;却在实际开发中相互配合&#xff0c;共同构建起高效、灵活的程序架构。在 DELPHI VCL 开发的技…

Docker 101:面向初学者的综合教程

掌握 Docker 已成为软件开发中的一项关键技能。本教程探讨了容器化的世界&#xff0c;包括其核心概念、优缺点&#xff0c;以及开始使用容器化的分步指南。 无论是 Docker 的新手&#xff0c;还是希望复习基础知识的更有经验的开发人员&#xff0c;本指南都能满足需求。 什么…

RTOS YAFFS

在 YAFFS (Yet Another Flash File System) 的语境中&#xff0c;“Check Point” 并不是一个标准的、核心的官方术语。它更可能是对 YAFFS 关键机制 Summary 或 Checkpointing 功能的非正式表述或理解偏差。其核心含义是指 YAFFS 在特定时刻保存文件系统关键元数据的状态&…

【SpringBoot系列-02】自动配置机制源码剖析

【SpringBoot系列-02】自动配置机制源码剖析 咱们天天用Spring Boot&#xff0c;一个SpringBootApplication注解扔进去&#xff0c;啥配置都不用写&#xff0c;项目就跑起来了。你有没有过这种疑惑&#xff1a;那些DispatcherServlet、DataSource是从哪冒出来的&#xff1f;今天…

51单片机-51单片机最小系统

本章概述思维导图&#xff1a;51单片机最小系统51单片机最小系统是51系列单片机&#xff08;如AT89C51、STC89C52等&#xff09;能够独立工作的最简电路配置&#xff0c;它为单片机提供了运行所需的基本条件。51单片机最小系统板是嵌入式系统开发的基础平台&#xff0c;集成了单…

git学习1

目录引入版本控制集中式和分布式版本控制git工作机制代码托管中心Git常用命令设置用户签名初始化本地库查看库状态add和提交版本穿梭git分支操作分支定义分支好处分支操作查看分支创建分支切换分支分支合并&#x1f495;✨&#x1fa77;合并冲突git团队协作团队内协作跨团队协作…

redis原理篇--Dict

Dict数据结构一、Redis字典的核心组件Redis字典由三部分构成&#xff1a;dictht&#xff08;哈希表&#xff09;&#xff1a;存储桶数组与元数据dictEntry&#xff08;哈希节点&#xff09;&#xff1a;存储键值对dict&#xff08;字典主体&#xff09;&#xff1a;包含双哈希表…

静态路由主备切换

在网络中&#xff0c;静态路由的主备切换是实现网络冗余的基础方案之一&#xff0c;通过配置不同优先级的静态路由&#xff0c;确保主用路径故障时&#xff0c;流量能自动切换到备用路径&#xff0c;提升网络可靠性。以下从知识讲解和实验配置两部分详细说明。一、静态路由主备…