Linked List Random Node

Linked List Random Node

Given a singly linked list, return a random node's value from the linked list. Each node must have the same probability of being chosen.

Implement the Solution class:

  • Solution(ListNode head) Initializes the object with the head of the singly-linked list head.
  • int getRandom() Chooses a node randomly from the list and returns its value. All the nodes of the list should be equally likely to be chosen.

Example 1:

scss 复制代码
 Input
 ["Solution", "getRandom", "getRandom", "getRandom", "getRandom", "getRandom"]
 [[[1, 2, 3]], [], [], [], [], []]
 Output
 [null, 1, 3, 2, 2, 3]
 ​
 Explanation
 Solution solution = new Solution([1, 2, 3]);
 solution.getRandom(); // return 1
 solution.getRandom(); // return 3
 solution.getRandom(); // return 2
 solution.getRandom(); // return 2
 solution.getRandom(); // return 3
 // getRandom() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning.

Constraints:

  • The number of nodes in the linked list will be in the range [1, 104].
  • -104 <= Node.val <= 104
  • At most 104 calls will be made to getRandom.

解法一:模拟、暴力

做法:

将链表的所有元素取出来,形成一个数组,在数组随机取数即可。

代码:

go 复制代码
 /**
  * Definition for singly-linked list.
  * type ListNode struct {
  *     Val int
  *     Next *ListNode
  * }
  */
 type Solution struct {
     nums []int
 }
 ​
 ​
 func Constructor(head *ListNode) Solution {
     nums:=[]int{}
     for head!=nil{
         nums=append(nums,head.Val)
         head=head.Next
     }
     return Solution{nums}
 }
 ​
 ​
 func (this *Solution) GetRandom() int {
     return this.nums[rand.Intn(len(this.nums))]
 }
 ​
 ​
 /**
  * Your Solution object will be instantiated and called as such:
  * obj := Constructor(head);
  * param_1 := obj.GetRandom();
  */

解法二:蓄水池抽样

具体做法为:从前往后处理每个样本,每个样本成为答案的概率为 \frac{1}{i},其中 i 为样本编号(编号从 111 开始),最终可以确保每个样本成为答案的概率均为 \frac{1}{n} (其中 n 为样本总数)。

证明:

目前已经有了 k 个元素,选中的概率是 \frac{1}{k} ,基于此加入更多的元素,最后全部元素中样本选择概率是 \frac{1}{n} (其中 n 为样本总数)。公式如下:
P = 1 k ∗ κ k + 1 ∗ κ + 1 k + 2 ∗ . . . ∗ n − 1 n P=\frac{1}{k}*\frac{\kappa}{k+1}*\frac{\kappa+1}{k+2}*...*\frac{n-1}{n} P=k1∗k+1κ∗k+2κ+1∗...∗nn−1

经过化简可以得:
P = 1 n P=\frac1n P=n1

实现代码如下:

go 复制代码
 /**
  * Definition for singly-linked list.
  * type ListNode struct {
  *     Val int
  *     Next *ListNode
  * }
  */
 type Solution struct {
     head *ListNode
 }
 ​
 ​
 func Constructor(head *ListNode) Solution {
     return Solution{head}
 }
 ​
 ​
 func (this *Solution) GetRandom() int {
     ans:=0
     for idx,head:=1,this.head; head!=nil;idx++{
         if rand.Intn(idx)==0{
             ans= head.Val
         }
         head=head.Next
     }
     return ans
 }
 ​
 ​
 /**
  * Your Solution object will be instantiated and called as such:
  * obj := Constructor(head);
  * param_1 := obj.GetRandom();
  */

问题一:为什么需要 rand.Intn(idx)==0 这个判断条件?

理解:因为是从第一个元素开始的,所以他的随机区间是 [0,1),所以是 需要等于 0。

问题二:为什么需要从一开始处理样本到最后?这里和公式哪里是一致的。

举个例子:

假设有 10 个元素,需要随机选择一个元素,在第五次处理元素时候,随机数值是 0,代表该选择的概率是 P=\frac{1}{5}\frac{1}{6} \frac{1}{7}...\frac{1}{10},最后计算可得概率是 \frac1{10}。如果将这一次的过程扩展到每一个元素呢?是不是每次随机到的元素的值是 \frac1{10}

接下来如果希望把这个定理进行扩展的话,定理描述如下:

参与样本总数量是 N,抽取样本数为 K,每个样本抽取的概率是 \frac{K}{N}。

参考如下:

蓄水池抽样算法------原理、实现与应用

相关推荐
mmmmath_33 小时前
LeetCode.199.二叉树的右视图
数据结构·leetcode
行百里er4 小时前
Redis 完结篇——Redis 热点问题在 Spring Boot 项目中实战
redis·后端
高频因子挖掘机5 小时前
只取最近 60 个交易日,却每次拉全历史:量化脚本变慢的原因与改法
后端·github·api
孟健6 小时前
从5000亿Token到83%字节匹配:AI逆向工程与重构的工程闭环实践
后端·架构·ai编程
码事漫谈7 小时前
丢得静悄悄,谈一谈 Agent 的记忆是怎么没的
后端
迪飞特科技7 小时前
智慧物业平台实战:Spring Boot 整合小程序实现报修巡检费用全流程
spring boot·后端·小程序
考虑考虑7 小时前
@JsonInclude注解使用
spring boot·后端·spring
IT_陈寒7 小时前
Vite的这个问题坑了我三天,原来如此解决!
前端·人工智能·后端
网易云信7 小时前
大模型能上岗做销售吗?我们让 12 款国产模型交了 420 份作业
前端·后端
CCblandy7 小时前
HTTP 长连接 3 类故障实测:[WinError 10053]、99 个 TIME_WAIT、69 次多余握手,4 种对策怎么选
后端·python·网络协议·http·云原生·性能优化·架构