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

参考如下:

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

相关推荐
黑色的山岗在沉睡4 分钟前
LeetCode 494. 目标和
算法·leetcode·职场和发展
追逐时光者5 小时前
推荐 12 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
Jagger_5 小时前
敏捷开发流程-精简版
前端·后端
苏打水com6 小时前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
间彧7 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧7 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧7 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧7 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧7 小时前
Spring Cloud Gateway详解与应用实战
后端
EnCi Zheng9 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端