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

这篇博客讨论了一种在大型数组中随机选取指定元素索引的算法。提供了三种不同的解决方案,包括使用映射、不创建额外切片和水塘抽样法。重点介绍了水塘抽样法,它在处理大数组时效率高且节省空间。博客解释了每种方法的工作原理,并通过例子展示了它们如何确保每个索引被选中的概率相等。此外,还探讨了这些方法在面对特定问题,如不允许创建额外数据结构或在未知文件大小情况下随机抽取行时的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
}

很容易理解哪个数会被返回是随机的,但是不是如题目所说每个数返回的概率都相等呢?
官方证明如下:
在这里插入图片描述
学过概率论应该不难理解。
第三种和第二种思路一样,只是在输出一个随机的下标的方式上有所不同。
第三种有一点难理解却是最推荐的解法,原因其实在题目的注意里有提到:

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值