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 为样本总数)。公式如下:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 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} </math>P=k1∗k+1κ∗k+2κ+1∗...∗nn−1

经过化简可以得:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> P = 1 n P=\frac1n </math>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}。

参考如下:

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

相关推荐
Victor3569 小时前
Redis(129)Redis的字典(Dictionary)是如何实现的?
后端
Victor3569 小时前
Redis(128)Redis的跳表(Skip List)是如何实现的?
后端
q***925116 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
m0_6398171516 小时前
基于springboot火锅店管理系统【带源码和文档】
java·spring boot·后端
pilgrim5317 小时前
结合 Leetcode 题探究KMP算法
算法·leetcode
会编程的林俊杰17 小时前
SpringBoot项目启动时的依赖处理
java·spring boot·后端
码事漫谈17 小时前
C++循环结构探微:深入理解while与do...while
后端
码事漫谈18 小时前
现代C++:一场静默的革命,告别“C with Classes”
后端
AntBlack18 小时前
AI Agent : CrewAI 简单使用 + 尝试一下股票分析
后端·python·ai编程
刘一说18 小时前
深入理解 Spring Boot 单元测试:从基础到最佳实践
spring boot·后端·单元测试