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();
這道題給了我們一個鏈表,讓隨機返回一個節點,那么最直接的方法就是先統計出鏈表的長度,然后根據長度隨機生成一個位置,然后從開頭遍歷到這個位置即可,參見代碼如下:
解法一:
class Solution { public: Solution(ListNode* head) { len = 0; ListNode *cur = head; this->head = head; while (cur) { ++len; cur = cur->next; } } int getRandom() { int t = rand() % len; ListNode *cur = head; while (t) { --t; cur = cur->next; } return cur->val; } private: int len; ListNode *head; };
Follow up 中說鏈表可能很長,我們沒法提前知道長度,這里用到了著名了 水塘抽樣 Reservoir Sampling 的思路,由於限定了 head 一定存在,所以先讓返回值 res 等於 head 的節點值,然后讓 cur 指向 head 的下一個節點,定義一個變量i,初始化為2,若 cur 不為空則開始循環,在 [0, i - 1] 中取一個隨機數,如果取出來0,則更新 res 為當前的 cur 的節點值,然后此時i自增一,cur 指向其下一個位置,這里其實相當於維護了一個大小為1的水塘,然后隨機數生成為0的話,交換水塘中的值和當前遍歷到的值,這樣可以保證每個數字的概率相等,參見代碼如下:
解法二:
class Solution { public: Solution(ListNode* head) { this->head = head; } int getRandom() { int res = head->val, i = 2; ListNode *cur = head->next; while (cur) { int j = rand() % i; if (j == 0) res = cur->val; ++i; cur = cur->next; } return res; } private: ListNode *head; };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/382
類似題目:
參考資料:
https://leetcode.com/problems/linked-list-random-node/
https://leetcode.com/problems/linked-list-random-node/discuss/85662/Java-Solution-with-cases-explain
