问题
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.
Follow up:
What if the linked list is extremely large and its length is unknown to you? Could you solve this efficiently without using extra space?
Example:
// Init a singly linked list [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() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning.
solution.getRandom();
思考
在不考虑follow up的情况下其实很简单. 都取出来,存在一个arraylist里面, 然后每次用随机数去取就可以了( 用java自带的random方法严格意义上说其实是伪随机数, pseudorandom number).
方法1
- Solution()
时间复杂度O(n).
空间复杂度O(n). - getRandom()
时间复杂度O(1).
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
private List<Integer> list = new ArrayList<Integer>();
private Random rnd = new Random();
/** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
public Solution(ListNode head) {
while(head != null){
list.add(head.val);
head = head.next;
}
}
/** Returns a random node's value. */
public int getRandom() {
return list.get(rnd.nextInt(list.size()));
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(head);
* int param_1 = obj.getRandom();
*/
方法2
如果来源数据特别大, 如何在固定空间复杂度的情况下实现随机取值呢?
Reservoir Sampling (蓄水池采样). 关于蓄水池采样的详细描述以后有时间可以再补上. 这里简单只做简单说明.
原始的算法是为了解决从n个元素中随机取k个样本.
- 先选取从1 到k 一共k个元素作为初始样本组.
- 从k + 1 开始取随机数(范围 1 到k +1), 如果随机数小于K+ 1 (假设为 j), 那么用 K+ 1 位的数, 替换掉第j位的数
- 循环上述2的处理,直到最后一位.
为什么这样的处理能做到随机呢? 也就是让每个元素被选中的概率是相等的呢? 证明过程有时间再补充.
- Solution()
时间复杂度O(1).
空间复杂度O(1). - getRandom()
时间复杂度O(n).
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
private ListNode head;
Random rnd = new Random();
/** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
public Solution(ListNode head) {
this.head = head;
}
/** Returns a random node's value. */
public int getRandom() {
int i = 1, res = this.head.val;
ListNode cur = this.head.next;
while(cur != null){
if(rnd.nextInt(i + 1) == i)
res = cur.val;
i++;
cur = cur.next;
}
return res;
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(head);
* int param_1 = obj.getRandom();
*/