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}。

参考如下:

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

相关推荐
ShadowYD27 分钟前
Agent Loop:一个让 AI 编程 Agent 从"会写代码"进化到"闭环交付"的开源 Skill(适配国内外主流 Code Agent)
后端
考虑考虑2 小时前
nohup启动java程序
后端·自动化运维
aramae3 小时前
模拟实现strcmp()(C语言)
c语言·开发语言·后端
根目录下的猫5 小时前
RK3588适配的轻量级AI模型推荐
人工智能·后端·python·目标检测
星栈6 小时前
用 Rust 写 Agent 服务 -- adk-rust 上手记
后端·agent
杨运交6 小时前
[071][验证码模块]基于Spring拦截器的验证码认证设计思想
java·后端·spring
圣保罗的大教堂6 小时前
leetcode 835. 图像重叠 中等
leetcode
LucianaiB7 小时前
我用豆包工作 Seed-2.1-pro,复刻了 QQ 时代爆红的魔术图片
后端
码事漫谈7 小时前
智谱 ZCode 静默上传 Git 历史:48 小时信任危机复盘
后端