382. 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.
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();
1、傳說中的蓄水池抽樣算法(此處水池大小爲1),共有N個元素,等概推導:假定第m個選中的概率爲1/m,則結果爲1/m*(1-1/(m+1))…….(1-1/(N-1))(1-1/N)=1/m(m/(1+m))……((N-2)/(N-1))*((N-1)/N)=1/N。可見他的出現是所有數字中等概出現的。至於爲什麼不算前面的,因爲我現在已經處理到了第m個,保存到了該值,只有後面的結果對他有影響。
2、隨機數產生:
uniform_int_distribution<unsigned> u(0,i);
default_random_engine e(rand());
unsigned int m=u(e);
第一條語句是分佈類型,閉區間[a,b];第二條語句是引擎;第三條映射(將分佈類型映射到指定引擎)。注意到給引擎的種子,是爲了不讓序列重複。本來可以用time(0),但發現循環不管用,後來用rand()發現不會,百度得知他會在動調用srand()。另外,對於static,他可以讓隨機數接二連三產生,而不是重複的從一個地方產生。但在這裏環境不能用,在vs裏面嘗試發現也不行,每次都會選中末尾元素,暫時還沒有找出原因。
3、蓄水池抽樣算法(水池大小爲K):此時我們需要先給水池填滿前K值,然後再由後續的值進行替換。思路都是一樣的,推導由前面的1換成K就好了。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
Solution(ListNode* head):cur(head) {
}
/** Returns a random node's value. */
int getRandom() {
int val=cur->val;
ListNode *temp=cur;
for(int i=0;temp!=nullptr;temp=temp->next,++i)
{
uniform_int_distribution<unsigned> u(0,i);
default_random_engine e(rand());//真正隨機的種子
unsigned int m=u(e);
if(m<1)
{
val=temp->val;
}
}
return val;
}
private:
ListNode *cur;
};
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(head);
* int param_1 = obj.getRandom();
*/