问题:
给定一个链表,求随机抽取一个元素。
使得每个元素被取出的概率相同。
Example 1: 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, 10^4]. -10^4 <= Node.val <= 10^4 At most 10^4 calls will be made to getRandom.
解法:math,概率,priority_queue
解法一:math,概率
从给定的链表中,取出一个数:
- 那么我们遍历这个链表的所有元素,
- 进行随机选取:
- 当遍历到第i个数,若满足一定条件,我们将这个数取出作为结果。
- 这个条件是:
- 实现:从[0~i)中取随机数,若该随机数==0,则取出当前数。这个概率== 1/i
- 不取出这个数作为结果(替换掉之前的结果)的概率==1-1/i
- 当遍历到第i个数,若满足一定条件,我们将这个数取出作为结果。
那么对于某个数 i,我们遍历完所有数,将其取得的概率即为:
- (我们只可能从遍历到 第 i 个数 开始)
- 当前取得这个数 i 作为结果,然后遍历其他数时,不再选取其他数来替代当前结果。
- 1/i * (1-1/(i+1)) * (1-1/(i+2)) *...* (1-1/n)
- = 1/i * (i/(i+1)) * ((i+1)/(i+2)) *...* ((n-1)/n)
- = 1/n
满足题意:概率相同都为1/n
代码参考:
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode() : val(0), next(nullptr) {} 7 * ListNode(int x) : val(x), next(nullptr) {} 8 * ListNode(int x, ListNode *next) : val(x), next(next) {} 9 * }; 10 */ 11 class Solution { 12 public: 13 ListNode* h; 14 /** @param head The linked list's head. 15 Note that the head is guaranteed to be not null, so it contains at least one node. */ 16 Solution(ListNode* head) { 17 h=head; 18 } 19 20 /** Returns a random node's value. */ 21 int getRandom() { 22 ListNode* p = h; 23 int res=p->val; 24 int i=0; 25 //cout<<res<<endl; 26 while(p) { 27 i++; 28 //cout<<","<<i; 29 int rd = rand() % i; 30 //cout<<rd<<endl; 31 if(rd==0) res = p->val; 32 //chosen probability=1/i; 33 //not chosen probability=1-1/i; 34 p=p->next; 35 } 36 // the probablity that k is chosen : 37 // from i==k to i==n, that k can be chosen. 38 // 1/k * (1-1/(k+1)) * (1-1/(k+2)) *...* (1-1/n) 39 //=1/k * (k/(k+1)) * ((k+1)/(k+2)) *...* ((n-1)/n) 40 //=1/n 41 return res; 42 } 43 }; 44 45 /** 46 * Your Solution object will be instantiated and called as such: 47 * Solution* obj = new Solution(head); 48 * int param_1 = obj->getRandom(); 49 */
解法二:priority_queue
每次抽取一个数时:
遍历所有的数,对每个数分配随机数,
并将这个随机数作为权值,加入优先队列,
遍历完毕后,出队一个元素(权值最大or最小),则为随机选取的数。
代码参考:
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode() : val(0), next(nullptr) {} 7 * ListNode(int x) : val(x), next(nullptr) {} 8 * ListNode(int x, ListNode *next) : val(x), next(next) {} 9 * }; 10 */ 11 typedef pair<int, int> pii; 12 class Solution { 13 public: 14 ListNode* h; 15 /** @param head The linked list's head. 16 Note that the head is guaranteed to be not null, so it contains at least one node. */ 17 Solution(ListNode* head) { 18 h=head; 19 } 20 21 /** Returns a random node's value. */ 22 int getRandom() { 23 ListNode* p = h; 24 priority_queue<pii, vector<pii>, greater<pii>> q; 25 while(p) { 26 int rd = rand(); 27 q.push({rd, p->val}); 28 p=p->next; 29 } 30 return q.top().second; 31 } 32 }; 33 34 /** 35 * Your Solution object will be instantiated and called as such: 36 * Solution* obj = new Solution(head); 37 * int param_1 = obj->getRandom(); 38 */