382. 链表随机节点
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?
示例:
// 初始化一个单链表 [1,2,3]. ListNode head = new ListNode(1); head.next = new ListNode(2); head.next.next = new ListNode(3); Solution solution = new Solution(head); // getRandom()方法应随机返回1,2,3中的一个,保证每个元素被返回的概率相等。 solution.getRandom();
大小为1的蓄水池抽样
class Solution { /** @param head The linked list's head. Note that the head is guaranteed to be not null, so it contains at least one node. */ ListNode head; public Solution(ListNode head) { this.head = head; } /** Returns a random node's value. */ public int getRandom() { int res = head.val; int i= 2; ListNode cur = head.next; while(cur!=null){ Random random = new Random(); int j = random.nextInt(i); if(j==0){ res = cur.val; } i++; cur = cur.next; } return res; } }
398 随机数索引
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。
注意:
数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。
示例:
int[] nums = new int[] {1,2,3,3,3}; Solution solution = new Solution(nums); // pick(3) 应该返回索引 2,3 或者 4。每个索引的返回概率应该相等。 solution.pick(3); // pick(1) 应该返回 0。因为只有nums[0]等于1。 solution.pick(1);
class Solution { int[] nums; public Solution(int[] nums) { this.nums = nums; } public int pick(int target) { int res = -1; int n=0; for(int i=0; i< nums.length; i++){ if(nums[i] == target){ Random random = new Random(); int j= random.nextInt(++n); if(j==0){ res = i; } } } return res; } }
【Reservoir Sampling 蓄水池抽样问题】
(可理解为为等概抽样问题)
-
问题:n个数中抽取k个,确保每个数被抽中的概率为n/k。
-
基本思路:
- 先选取1,2,3,...,k将之放入蓄水池;
- 对于k+1,将之以k/(k+1)的概率抽取,然后随机替换水池中的一个数。
- 对于k+i,将之以k/(k+i)的概率抽取,然后随机替换水池中的一个数。
- 重复上述,直到k+i到达n;
-
证明:
对于k+i,其选中并替换水池中已有元素的概率为k/(k+i)
对于水池中的某数x,其之前就在水池,一次替换后仍在水池中的概率是
P(x之前在水池) * P(未被k+i替换)
=P(x之前在水池) * (1-P(k+i被选中且替换了x) )
= k/(k+i-1) × (1 - k/(k+i) × 1/k)
= k/(k+i)
当k+i到达n,则结果为k/n