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 listhead
.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 togetRandom
.
解法一:模拟、暴力
做法:
将链表的所有元素取出来,形成一个数组,在数组随机取数即可。
代码:
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}。
参考如下: