当前位置: 首页 > news >正文

DeepSeek LeetCode 710. 黑名单中的随机数 public Solution(int n, int[] blacklist) Java实现

LeetCode 710. 黑名单中的随机数 详细题解

问题分析

这道题的核心是:在[0, n-1]范围内随机选择一个不在黑名单中的数字。要求:

  1. pick()方法要等概率返回白名单中的数字
  2. 不能直接创建白名单数组(可能太大)
  3. 需要优化时间和空间复杂度

解法一:映射法(最优解)

classSolution{privateMap<Integer,Integer>mapping;privateRandomrandom;privateintvalidRange;// 实际有效的随机范围publicSolution(intn,int[]blacklist){mapping=newHashMap<>();random=newRandom();// 将黑名单中的数字存入Set,方便快速查找Set<Integer>blackSet=newHashSet<>();for(intb:blacklist){blackSet.add(b);}// 有效的随机范围是 [0, n - blacklist.length - 1]validRange=n-blacklist.length;// 处理黑名单中的数字// 思路:将 [0, validRange-1] 范围内的黑名单数字,// 映射到 [validRange, n-1] 范围内的白名单数字// 先找出所有在 [validRange, n-1] 范围内的黑名单数字Set<Integer>blackInValidRange=newHashSet<>();for(intb:blacklist){if(b>=validRange){blackInValidRange.add(b);}}// 为 [0, validRange-1] 范围内的黑名单数字建立映射intwhiteIndex=validRange;// 从 validRange 开始找白名单数字for(intb:blacklist){if(b<validRange){// 跳过也在黑名单中的数字while(blackInValidRange.contains(whiteIndex)){whiteIndex++;}// 将黑名单数字映射到白名单数字mapping.put(b,whiteIndex);whiteIndex++;}}}publicintpick(){intrand=random.nextInt(validRange);// 如果随机到的数字在黑名单中,返回映射值// 否则直接返回原数字returnmapping.getOrDefault(rand,rand);}}

时间复杂度

  • 构造函数:O(m),m 是黑名单长度
  • pick():O(1)

空间复杂度:O(m)

解法二:二分查找法

classSolution{privateint[]sortedBlacklist;privateRandomrandom;privateintn;publicSolution(intn,int[]blacklist){Arrays.sort(blacklist);this.sortedBlacklist=blacklist;this.random=newRandom();this.n=n;}publicintpick(){intrand=random.nextInt(n-sortedBlacklist.length);// 使用二分查找确定随机数需要跳过多少个黑名单数字intleft=0,right=sortedBlacklist.length-1;while(left<=right){intmid=left+(right-left)/2;// mid位置的黑名单数字,前面有mid个黑名单数字// 所以它实际对应的白名单位置是 sortedBlacklist[mid] - midif(sortedBlacklist[mid]-mid<=rand){left=mid+1;}else{right=mid-1;}}// left 表示需要跳过的黑名单数量returnrand+left;}}

时间复杂度

  • 构造函数:O(m log m)(排序)
  • pick():O(log m)

空间复杂度:O(m)

解法三:列表法(简单但空间大)

classSolution{privateList<Integer>whitelist;privateRandomrandom;publicSolution(intn,int[]blacklist){Set<Integer>blackSet=newHashSet<>();for(intb:blacklist){blackSet.add(b);}whitelist=newArrayList<>();for(inti=0;i<n;i++){if(!blackSet.contains(i)){whitelist.add(i);}}random=newRandom();}publicintpick(){intindex=random.nextInt(whitelist.size());returnwhitelist.get(index);}}

时间复杂度

  • 构造函数:O(n)
  • pick():O(1)

空间复杂度:O(n - m)

缺点:当 n 很大时(如 10^9),无法创建这么大的列表

解法四:完整实现(包含边界情况处理)

classSolution{privateMap<Integer,Integer>mapping;privateRandomrandom;privateintvalidRange;publicSolution(intn,int[]blacklist){mapping=newHashMap<>();random=newRandom();// 处理边界情况if(blacklist==null){blacklist=newint[0];}// 排序黑名单,便于处理Arrays.sort(blacklist);// 有效的随机范围validRange=n-blacklist.length;// 将黑名单分成两部分:// 1. 在 [0, validRange-1] 内的黑名单数字(需要映射)// 2. 在 [validRange, n-1] 内的黑名单数字(直接忽略)// 找出后半部分的黑名单数字Set<Integer>highBlack=newHashSet<>();for(intb:blacklist){if(b>=validRange){highBlack.add(b);}}// 为前半部分的黑名单数字建立映射intcurrentHigh=validRange;for(intb:blacklist){if(b<validRange){// 找到下一个不在黑名单中的数字while(highBlack.contains(currentHigh)){currentHigh++;}mapping.put(b,currentHigh);currentHigh++;}}}publicintpick(){intrand=random.nextInt(validRange);// 如果随机到的数字在黑名单中,返回映射值// 否则直接返回原数字Integermapped=mapping.get(rand);returnmapped!=null?mapped:rand;}}/** * Your Solution object will be instantiated and called as such: * Solution obj = new Solution(n, blacklist); * int param_1 = obj.pick(); */

解法对比

解法时间复杂度空间复杂度优点缺点
映射法O(m) / O(1)O(m)最优解,pick O(1)实现稍复杂
二分法O(m log m) / O(log m)O(m)空间效率好pick O(log m)
列表法O(n) / O(1)O(n-m)实现最简单n 很大时不可行

核心思路详解

  1. 问题本质:在[0, n-1]中随机选数,排除黑名单

  2. 关键观察

    • 总共有n - blacklist.length个白名单数字
    • 我们可以将随机范围缩小到[0, n - blacklist.length - 1]
    • 将黑名单中的数字映射到后半部分的白名单数字
  3. 映射法原理

    范围划分: [0, validRange-1] | [validRange, n-1] 随机选择范围 | 映射目标范围 黑名单数字分为两类: - 在左边的:需要映射到右边 - 在右边的:已经被排除,不需要映射
  4. 例子说明

    n = 6, blacklist = [0, 2, 3] validRange = 3 (6 - 3 = 3) 映射: 0 (左边黑名单) -> 3 (右边第一个白名单) 2 (左边黑名单) -> 4 (右边第二个白名单) 3 (左边黑名单) -> 5 (右边第三个白名单) 最终: pick(0) -> 映射到 3 pick(1) -> 直接返回 1(白名单) pick(2) -> 映射到 4

边界情况处理

  1. 空黑名单:直接返回随机数
  2. 黑名单包含所有数字:validRange = 0,pick 返回 -1 或抛出异常
  3. n 很大:确保不使用 O(n) 空间
  4. 黑名单未排序:需要排序或使用 HashSet

面试建议

  • 首选解法:映射法(解法一)
  • 解释思路:先说明不能创建白名单数组的原因(空间限制),然后解释如何通过映射来解决问题
  • 优化点:说明为什么要将黑名单分成两部分处理
  • 边界情况:主动提及要考虑的边界情况
http://www.jsqmd.com/news/494352/

相关文章:

  • 个人笔记机器学习2
  • 70.爬楼梯
  • R ∪ S(并集)**:正确合并了 R 和 S 的所有元组,并去重((b,a,d) 和 (d,f,g) 在两者中均出现,只保留一次
  • 【ZooKeeper】 ZooKeeper面试必知必会:从基础到进阶的全方位指南
  • SEH详解(六)
  • PCIe-FC补充《PCI Express Technology 3.0》Chapter 6 Flow Control
  • Netty[ NIO 核心速成 ] ---- NIO三大组件(Channel Bufferselector)
  • AI赋能森林火防助力开启智慧守护新篇章,基于最新以注意力为核心的YOLOv12全系列【n/s/m/l/x】参数模型开发构建AI智能化森林火防无人机巡检场景下森林火点、烟雾异常检测预警系统
  • 走上管理岗,一定要学会立威
  • java工具:《Java字符串处理:如何获取指定字符第N次出现的位置?》
  • STM32 学习 —— 个人学习笔记9-1(USART串口协议 串口发送及接收数据)
  • 461.汉明距离
  • 附录A 游戏推广运营实战:《暗黑王朝》的市场化之路
  • GESP三级历年真题解析(原码、反码和补码)
  • leetcode 困难题 1402. Reducing Dishes 做菜顺序
  • 2026年热门的AI品牌管理品牌推荐:AI品牌管理系统/AI品牌营销管理系统实力公司推荐 - 品牌宣传支持者
  • leetcode 1405. Longest Happy String 最长快乐字符串-耗时100
  • 计算机毕设 java 梅州红色文化传承小程序 Java+SpringBoot 梅州红色文化小程序 微信小程序红色文化传承平台
  • 2026 独立开发者 AI 工具栈:我的选择和理由
  • 从交易者到“合伙人”:Cber经纪人体系全解析,你的每一份共识都算数
  • 5个免费IP查询API对比:哪个最适合你的项目?(附性能测试数据)
  • ChatTTS下载安装全攻略:从原理到避坑指南
  • 2026年知名的AI品牌视频公司推荐:AI品牌宣传片/AI品牌营销管理/AI品牌营销管理系统品牌公司推荐 - 品牌宣传支持者
  • FreeRTOS工程项目实践
  • 计算机毕设 java 美文推荐系统 Java+SpringBoot 美文推荐分享平台 Web 版美文博文交流网站
  • 基于计算机视觉的万物识别模型性能优化策略
  • 2026年口碑好的电热风炉厂家推荐:矿用电热风炉/井口防冻电热风炉源头工厂推荐 - 品牌宣传支持者
  • Unity开发次世代写实手游开发大纲
  • leetcode 困难题 1406. Stone Game III 石子游戏 III
  • sql性能分析和sql优化