Leetcode 剑指 Offer II 071.按权重随机选择

题目难度: 中等

原题链接

今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 i 的概率与 w[i] 成正比。

例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。

也就是说,选取下标 i 的概率为 w[i] / sum(w) 。

示例 1:

  • 输入:
    • inputs = [“Solution”,“pickIndex”]
    • inputs = [[[1]],[]]
  • 输出:
    • [null,0]
  • 解释:
    • Solution solution = new Solution([1]);
    • solution.pickIndex(); // 返回 0,因为数组中只有一个元素,所以唯一的选择是返回下标 0。

示例 2:

  • 输入:
    • inputs = [“Solution”,“pickIndex”,“pickIndex”,“pickIndex”,“pickIndex”,“pickIndex”]
    • inputs = [[[1,3]],[],[],[],[],[]]
  • 输出:
    • [null,1,1,1,1,0]
  • 解释:
    • Solution solution = new Solution([1, 3]);
    • solution.pickIndex(); // 返回 1,返回下标 1,返回该下标概率为 3/4 。
    • solution.pickIndex(); // 返回 1
    • solution.pickIndex(); // 返回 1
    • solution.pickIndex(); // 返回 1
    • solution.pickIndex(); // 返回 0,返回下标 0,返回该下标概率为 1/4 。
    • 由于这是一个随机问题,允许多个答案,因此下列输出都可以被认为是正确的:
    • [null,1,1,1,1,0]
    • [null,1,1,1,1,1]
    • [null,1,1,1,0,0]
    • [null,1,1,1,0,1]
    • [null,1,0,1,0,0]
    • 诸若此类。

提示:

  • 1 <= w.length <= 10000
  • 1 <= w[i] <= 10^5
  • pickIndex 将被调用不超过 10000 次

题目思考

  1. 如何设计随机算法?
  2. 如何优化时间复杂度?

解决方案

思路
  • 分析题目, 要考虑每个下标的权重, 我们显然不能直接随机取[0,n)的下标, 这样会导致每个下标取到的概率相同, 如何解决呢?
  • 根据题目例子, 我们可以发现, 每个下标的取值概率等于其权重除以权重总和
  • 利用这一点, 假设权重总和是 total, 我们随机取[0,total)之间的某个数字, 然后判断它落在了哪个下标的范围内, 这样就把下标权重考虑在内了
  • 举个例子, 假设数组 w 是[3,2,4], 那么总权重和就是3+2+4=9
  • 下标 0 的权重是 3, 它负责的范围是[0,2]
  • 下标 1 的权重是 2, 它负责的范围是[3,4]
  • 下标 2 的权重是 4, 它负责的范围是[5,8]
  • 我们随机取[0,9)之间的某个数字, 它落在哪个下标范围就返回哪个下标, 这样就保证了每个下标的取值概率等于其权重除以权重总和
  • 有了思路后, 如何写出代码呢?
  • 不难发现每个下标的负责范围的起点就是其前缀和, 即[0,3,5,9], 注意最后的 9 代表了总和, 不属于某个下标起点
  • 所以我们可以在初始化时求出原始数组对应的前缀和数组 sms, 最后一个元素sms[-1]就是总和
  • 然后 pickIndex 时随机取[0,sms[-1])之间的数字 x, 并从头开始遍历前缀和数组, 找到第一个满足sms[i]<=x<sms[i+1]的下标 i, 即为考虑权重后随机选取的下标
  • 不过上述查找过程需要线性遍历, 时间复杂度是 O(N), 能否继续优化呢?
  • 注意到题目给出的是正整数数组, 所以前缀和是单调递增的, 我们可以利用二分查找来优化
  • 这里我们能够完美利用 Python 提供的右二分 bisect_right, 它的语义是:
    • 如果数字存在于查找数组, 返回对应下标加 1
    • 如果数字不存在, 则返回首个大于该数字的下标或数组长度(如果已有数字都不大于查找数字时)
  • 这样右二分查找到的下标正是所需下标的下一个下标, 同样拿上面例子举例:
    • 假如查找数字是 0, 则右二分返回下标 1, 所需下标是 0
    • 假如查找数字是 1, 则右二分返回下标 1, 所需下标是 0
    • 假如查找数字是 2, 则右二分返回下标 1, 所需下标是 0
    • 假如查找数字是 3, 则右二分返回下标 2, 所需下标是 1
    • 假如查找数字是 4, 则右二分返回下标 2, 所需下标是 1
  • 大家如果感兴趣的话也可以自己尝试实现这个二分查找的过程, 基本类似前面的题目Leetcode 剑指 Offer II 068.搜索插入位置, 只需要稍作改动, target 存在时返回对应下标加 1, 而不是下标本身
  • 下面代码中有详细的注释, 方便大家理解
复杂度
  • 时间复杂度 O(logN): 每次 pickIndex 使用二分查找, 时间复杂度是 O(logN)
  • 空间复杂度 O(N): 需要额外存储前缀和数组
代码
class Solution:def __init__(self, w: List[int]):# 前缀和+二分查找self.sms = [0]for x in w:# 统计前缀和数组self.sms.append(x + self.sms[-1])def pickIndex(self) -> int:# 随机选择[0,数字总和)之间的一个数字xx = random.randrange(0, self.sms[-1])# 二分查找当前数字落在了哪个下标的范围内# 这里使用右二分bisect_right, 且二分得到的下标要减1return bisect.bisect_right(self.sms, x) - 1

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

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

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

相关文章

基于java+springboot+vue实现的电商个性化推荐系统(文末源码+Lw+ppt)23-389

摘 要 伴随着我国社会的发展&#xff0c;人民生活质量日益提高。于是对电商个性化推荐进行规范而严格是十分有必要的&#xff0c;所以许许多多的信息管理系统应运而生。此时单靠人力应对这些事务就显得有些力不从心了。所以本论文将设计一套电商个性化推荐系统&#xff0c;帮…

nvidia-smi查看无进程,但GPU占用率100%问题解决

问题&#xff1a;nvidia-smi查看无进程&#xff0c;但GPU占用率100%问题解决 原因&#xff1a;记住记住记住CtrlZ是把当前运行程序挂起&#xff0c;并不是终止运行&#xff0c;终止用CtrlC,前段时间跑代码测性能和看部分结果一直用的CtrlZ&#xff0c;导致程序都处于挂起状态&…

适用于 Windows 的 6 个最佳视频转换器

视频转换器可以帮助您在设备上转换和播放不受支持的视频格式。它还可以方便地减小视频文件大小、以通用格式组织所有视频或与其他人共享文件以在不同设备上播放。 Windows 有大量视频转换器可供选择。虽然有些是免费的&#xff0c;但其他一些则提供迎合专业用户的高级功能。在…

数据结构——线性表(二)

线性表顺序存储结构的优缺点 优点:1.无须为表示表中元素之间的逻辑关系而增加额外的存储空间 2.可以快速的存取表中的任一位置的元素 缺点:1.插入和删除操作需要移动大量的元素 2.当线性表长度变化较大的时候,难以确定存储空间的容量 3.造成存储空间的"碎片" 所以…

Go语言学习Day2:注释与变量

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1、注释①为什么要写注释&#xff1f;②单行注释…

C++11入门手册第一节,学完直接上手Qt(共两节)

入门 hello.cpp #include <iostream>int main() { std::cout << "Hello Quick Reference\n"<<endl; return 0;} 编译运行 $ g hello.cpp -o hello$ ./hello​Hello Quick Reference 变量 int number 5; // 整数float f 0.95; //…

双向长短期BiLSTM的回归预测-附MATLAB代码

BiLSTM是一种带有正反向连接的长短期记忆网络&#xff08;LSTM&#xff09;。 BiLSTM通过两个独立的LSTM层&#xff0c;一个按时间顺序处理输入&#xff0c;另一个按时间倒序处理输入&#xff0c;分别从正向和反向两个方向捕捉输入序列的特征。具体地&#xff0c;正向LSTM按时…

资源免费分享了

为了解决粉丝们在学习过程中&#xff0c;出现没有资料&#xff0c;没有书籍&#xff0c;搜索引擎搜索不精准&#xff0c;没有恰当的博文等这一类情况&#xff0c;今天&#xff0c;我将我曾经的学习资料&#xff0c;免费分享给大家&#xff01; 博主声明:此并非过度宣传&#x…

网络安全-内网DNS劫持-ettercap

前言 一&#xff0c;我也是初学者记录的笔记 二&#xff0c;可能有错误的地方&#xff0c;请谨慎 三&#xff0c;欢迎各路大神指教 四&#xff0c;任何文章仅作为学习使用 五&#xff0c;学习网络安全知识请勿适用于违法行为 学习网络安全知识请勿适用于违法行为 学习网络安全…

.Net 知识杂记

记录平日中琐碎的.net 知识点。不定期更新 目标框架名称(TFM) 我们创建C#应用程序时&#xff0c;在项目的工程文件(*.csproj)中都有targetFramework标签&#xff0c;以表示项目使用的目标框架 各种版本的TFM .NET Framework .NET Standard .NET5 及更高版本 UMP等 参考文档&a…

python实现两个Excel表格数据对比、补充、交叉验证

业务背景 业务中需要用到类似企查查一类的数据平台进行数据导出&#xff0c;但企查查数据不一定精准&#xff0c;所以想采用另一个官方数据平台进行数据对比核验&#xff0c;企查查数据缺少的则补充&#xff0c;数据一致的保留企查查数据&#xff0c;不一致的进行颜色标注。 …

【LeetCode: 面试题 16.05. 阶乘尾数 + 阶乘】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…