给你一个单链表,随机选择链表的一个节点,并返回相应的节点值。每个节点 被选中的概率一样 。
实现 Solution
类:
Solution(ListNode head)
使用整数数组初始化对象。
int getRandom()
从链表中随机选择一个节点并返回该节点的值。链表中所有节点被选中的概率相等。
示例:
**输入**
["Solution", "getRandom", "getRandom", "getRandom", "getRandom", "getRandom"]
[[[1, 2, 3]], [], [], [], [], []]
**输出**
[null, 1, 3, 2, 2, 3]
**解释**
Solution solution = new Solution([1, 2, 3]);
solution.getRandom(); // 返回 1
solution.getRandom(); // 返回 3
solution.getRandom(); // 返回 2
solution.getRandom(); // 返回 2
solution.getRandom(); // 返回 3
// getRandom() 方法应随机返回 1、2、3中的一个,每个元素被返回的概率相等。
提示:
链表中的节点数在范围 [1, 104]
内
-104 <= Node.val <= 104
至多调用 getRandom
方法 104
次
进阶:
如果链表非常大且长度未知,该怎么处理?
你能否在不使用额外空间的情况下解决此问题?
方法一:记录所有链表元素 我们可以在初始化时,用一个数组记录链表中的所有元素,这样随机选择链表的一个节点,就变成在数组中随机选择一个元素。
[sol1-Python3] 1 2 3 4 5 6 7 8 9 class Solution : def __init__ (self, head: Optional [ListNode] ): self.arr = [] while head: self.arr.append(head.val) head = head.next def getRandom (self ) -> int : return choice(self.arr)
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { vector<int > arr; public : Solution (ListNode *head) { while (head) { arr.emplace_back (head->val); head = head->next; } } int getRandom () { return arr[rand () % arr.size ()]; } };
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { List<Integer> list; Random random; public Solution (ListNode head) { list = new ArrayList <Integer>(); while (head != null ) { list.add(head.val); head = head.next; } random = new Random (); } public int getRandom () { return list.get(random.nextInt(list.size())); } }
[sol1-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Solution { IList<int > list; Random random; public Solution (ListNode head ) { list = new List<int >(); while (head != null ) { list.Add(head.val); head = head.next; } random = new Random(); } public int GetRandom () { return list[random.Next(list.Count)]; } }
[sol1-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 type Solution []int func Constructor (head *ListNode) (s Solution) { for node := head; node != nil ; node = node.Next { s = append (s, node.Val) } return s } func (s Solution) GetRandom() int { return s[rand.Intn(len (s))] }
[sol1-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 typedef struct { int * arr; int length; } Solution; Solution* solutionCreate (struct ListNode* head) { Solution * obj = (Solution *)malloc (sizeof (Solution)); assert(obj != NULL ); obj->length = 0 ; struct ListNode * node = head; while (node) { node = node->next; obj->length++; } obj->arr = (int *)malloc (sizeof (int ) * obj->length); assert(obj->arr != NULL ); node = head; for (int i = 0 ; i < obj->length; i++) { obj->arr[i] = node->val; node = node->next; } return obj; } int solutionGetRandom (Solution* obj) { return obj->arr[rand() % obj->length]; } void solutionFree (Solution* obj) { free (obj->arr); free (obj); }
[sol1-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 var Solution = function (head ) { this .list = []; while (head != null ) { this .list .push (head.val ); head = head.next ; } }; Solution .prototype .getRandom = function ( ) { return this .list [Math .floor (Math .random () * this .list .length )]; };
复杂度分析
方法二:水塘抽样 方法一需要花费 $O(n)$ 的空间存储链表中的所有元素,那么能否做到 $O(1)$ 的空间复杂度呢?
我们可以设计如下算法:
从链表头开始,遍历整个链表,对遍历到的第 $i$ 个节点,随机选择区间 $[0,i)$ 内的一个整数,如果其等于 $0$,则将答案置为该节点值,否则答案不变。
该算法会保证每个节点的值成为最后被返回的值的概率均为 $\dfrac{1}{n,证明如下:
$$ \begin{aligned} &P(第\ i\ 个节点的值成为最后被返回的值)\ =&P(第\ i\ 次随机选择的值= 0) \times P(第\ i+1\ 次随机选择的值\ne 0) \times \cdots \times P(第\ n\ 次随机选择的值\ne 0)\ =&\dfrac{1}{i} \times (1-\dfrac{1}{i+1}) \times \cdots \times (1-\dfrac{1}{n})\ =&\dfrac{1}{i} \times \dfrac{i}{i+1} \times \cdots \times \dfrac{n-1}{n}\ =&\dfrac{1}{n} \end{aligned} $$
[sol2-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 class Solution : def __init__ (self, head: Optional [ListNode] ): self.head = head def getRandom (self ) -> int : node, i, ans = self.head, 1 , 0 while node: if randrange(i) == 0 : ans = node.val i += 1 node = node.next return ans
[sol2-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { ListNode *head; public : Solution (ListNode *head) { this ->head = head; } int getRandom () { int i = 1 , ans = 0 ; for (auto node = head; node; node = node->next) { if (rand () % i == 0 ) { ans = node->val; } ++i; } return ans; } };
[sol2-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { ListNode head; Random random; public Solution (ListNode head) { this .head = head; random = new Random (); } public int getRandom () { int i = 1 , ans = 0 ; for (ListNode node = head; node != null ; node = node.next) { if (random.nextInt(i) == 0 ) { ans = node.val; } ++i; } return ans; } }
[sol2-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Solution { ListNode head; Random random; public Solution (ListNode head ) { this .head = head; random = new Random(); } public int GetRandom () { int i = 1 , ans = 0 ; for (ListNode node = head; node != null ; node = node.next) { if (random.Next(i) == 0 ) { ans = node.val; } ++i; } return ans; } }
[sol2-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type Solution struct { head *ListNode } func Constructor (head *ListNode) Solution { return Solution{head} } func (s *Solution) GetRandom() (ans int ) { for node, i := s.head, 1 ; node != nil ; node = node.Next { if rand.Intn(i) == 0 { ans = node.Val } i++ } return }
[sol2-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 typedef struct { struct ListNode * head ; } Solution; Solution* solutionCreate (struct ListNode* head) { Solution * obj = (Solution *)malloc (sizeof (Solution)); assert(obj != NULL ); obj->head = head; return obj; } int solutionGetRandom (Solution* obj) { int i = 1 , ans = 0 ; for (struct ListNode * node = obj->head; node; node = node->next) { if (rand() % i == 0 ) { ans = node->val; } ++i; } return ans; } void solutionFree (Solution* obj) { free (obj); }
[sol2-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var Solution = function (head ) { this .head = head; }; Solution .prototype .getRandom = function ( ) { let i = 1 , ans = 0 ; for (let node = this .head ; node != null ; node = node.next ) { if (Math .floor (Math .random () * i) === 0 ) { ans = node.val ; } ++i; } return ans; };
复杂度分析