382. 链表随机节点
简单看下暴力做法,使用一个数组存储所有的链表数据:
class Solution {List<Integer> list = new ArrayList<>();Random random = new Random(20220116);public Solution(ListNode head) {while (head != null) {list.add(head.val);head = head.next;}}public int getRandom() {int idx = random.nextInt(list.size());return list.get(idx);}}
蓄水池抽样的思路(学习三叶姐):
这算法的核心是对无限的数据流进行抽样,具体的做法为,从所有样本中抽取若干个,要求每个样本被抽到的概率相等。
具体做法为:从前往后处理每个样本,每个样本成为答案的概率为 (注意,考虑的是一个无限的流),其中
**i** 为样本编号(编号从 1 开始),最终可以确保每个样本成为答案的概率均为 (其中
**n** 为样本总数)。
看代码:
struct Solution {head: Option<Box<ListNode>>}/*** `&self` means the method takes an immutable reference.* If you need a mutable reference, change it to `&mut self` instead.*/impl Solution {fn new(head: Option<Box<ListNode>>) -> Self {Self{head}}fn get_random(&self) -> i32 {use rand::prelude::*;let (mut res, mut idx, mut head) = (-1, 1, &self.head);while let Some(node) = head {// 从第一个节点开始筛选,扔一个随机数,如果为0的时候// 意味着甩到的概率为1/idx,也就意味着我们选目前的这个元素// 其概率为1/idx,在延到无穷流中,也能够满足1/n的概率if thread_rng().gen_range(0, idx) == 0 { res = node.val; }idx+=1;head = &node.next;}res}}
