LeetCode每日一题(9)——随机数索引(理解水塘抽样)

简介: 随机数索引1.题目2.示例3.思路及代码

1.题目


给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。


注意:

数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。


2.示例


int[] nums = new int[] {1,2,3,3,3}; Solution solution = new

Solution(nums);


// pick(3) 应该返回索引 2,3 或者 4。每个索引的返回概率应该相等。

solution.pick(3);


// pick(1) 应该返回 0。因为只有nums[0]等于1。

solution.pick(1);


3.思路及代码


不推荐(可以增加对map使用的熟练程度)


// Solution 根据题意自定义类型,用来保存键(int类型,题目所给数组中的数字)值(切片,用来放下标们)对
type Solution map[int][]int
// Constructor 遍历数组,将每个数字和对应下标以键值对的方式存入pos
func Constructor(nums []int) Solution {
  pos := map[int][]int{}
  for i, v := range nums {
    //map的键是唯一的,面对重复的数字append只会多记录一条对应的下标
    //比如{1,2,3,3,3}中3的值键值对为 3:[2 3 4]
    pos[v] = append(pos[v], i)
  }
  return pos
}
// Pick 最后根据键(所给数字)找对应的值(保存下标们的切片)
// 再从找到的切片中返回其中的一个索引,用rand.Intn(len(indices))实现每个索引的返回概率应该相等
func (pos Solution) Pick(target int) int {
  indices := pos[target]
  return indices[rand.Intn(len(indices))]
}


较推荐(思路清晰且效率高)


type Solution []int
func Constructor(nums []int) Solution {
  return nums
}
func (nums Solution) Pick(target int) (ans int) {
  //创建了用来存放下标的切片
  var find []int
  for i, num := range nums {
    if num == target {
      find = append(find, i)
      }
    }
  if len(find)==1 {
    //题目说可以假设给定的数字一定存在于数组中,所以只有一个是0
    return find[0]
  }else {
    //题目说可以假设给定的数字一定存在于数组中,所以len(find)>0
    return find[rand.Intn(len(find))]
  }
}


推荐(水塘抽样法,效率高且可以解决更特殊的问题)


type Solution []int
func Constructor(nums []int) Solution {
  return nums
}
func (nums Solution) Pick(target int) (ans int) {
  //没有创建切片
  count := 0
  for i, num := range nums {
    if num == target {
      //遇到目标数字的次数+1
      count++
      //以下就是水塘抽样需要理解的地方
      //每次遇到目标数字都生成一个0到遇到次数之间的随机数
      //如果是0我愿称之为命中,那么ans被暂时设为这个num的下标
      //如果后面再也没有被命中的数字,那么ans就会被返回了
      if rand.Intn(count) == 0 {
        ans = i
      }
    }
  }
  return
}


很容易理解哪个数会被返回是随机的,但是不是如题目所说每个数返回的概率都相等呢?

官方证明如下:


c1a5d383ff8d4a988fa8e10eab01f0bd.png


学过概率论应该不难理解。

第三种和第二种思路一样,只是在输出一个随机的下标的方式上有所不同。

第三种有一点难理解却是最推荐的解法,原因其实在题目的注意里有提到:


数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。


前两种方法都有创建切片,不过也通过了。

但是在面对"不允许创建切片"或者"在不知道文件总行数的情况下,如何从文件中随机的抽取一行?"这种问题时,水塘抽样就香香了。


相关文章
|
25天前
|
索引
【LeetCode刷题】二分查找:山脉数组的峰顶索引、寻找峰值
【LeetCode刷题】二分查找:山脉数组的峰顶索引、寻找峰值
|
29天前
|
算法 数据挖掘 开发者
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
|
2月前
|
索引
Leetcode 给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引
Leetcode 给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引
|
9月前
|
人工智能 BI 索引
【Leetcode -598.范围求和Ⅱ -599.两个列表的最小索引总和】
【Leetcode -598.范围求和Ⅱ -599.两个列表的最小索引总和】
29 0
|
11月前
|
索引
LeetCode-398 随机数索引
LeetCode-398 随机数索引
|
2月前
|
开发者 索引 Python
【python刷题】LeetCode 2057E 值相等的最小索引(5种简单高效的解法)
【python刷题】LeetCode 2057E 值相等的最小索引(5种简单高效的解法)
35 0
|
索引 Python
LeetCode 599. 两个列表的最小索引总和
假设Andy和Doris想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示。
79 0
|
存储 索引
LeetCode——398. 随机数索引
LeetCode——398. 随机数索引
77 0
|
24天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
24天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-1
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题