「每日LeetCode」2023年3月7日

本文最后更新于:2023年3月19日 晚上

384.打乱数组

384.打乱数组

Category Difficulty Likes Dislikes
algorithms Medium (61.49%) 323 -

Tags
Companies
给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。打乱后,数组的所有排列应该是 等可能 的。
实现 Solution class:

  • Solution(int[] nums) 使用整数数组 nums 初始化对象
  • int[] reset() 重设数组到它的初始状态并返回
  • int[] shuffle() 返回数组随机打乱后的结果

示例 1:
输入 [“Solution”, “shuffle”, “reset”, “shuffle”] [[[1, 2, 3]], [], [], []] 输出 [null, [3, 1, 2], [1, 2, 3], [1, 3, 2]] 解释 Solution solution = new Solution([1, 2, 3]); solution.shuffle(); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2] solution.reset(); // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3] solution.shuffle(); // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

提示:

  • 1 <= nums.length <= 50
  • -106 <= nums[i] <= 106
  • nums 中的所有元素都是 唯一的
  • 最多可以调用 104 次 reset 和 shuffle

Discussion | Solution

思路

洗牌算法

解答

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
34
35
36
37
38
39
40
41
42
43
/*
* @lc app=leetcode.cn id=384 lang=javascript
*
* [384] 打乱数组
*/

// @lc code=start
/**
* @param {number[]} nums
*/
var Solution = function (nums) {
this.nums = nums;
this.originNums = nums.slice();
};

/**
* @return {number[]}
*/
Solution.prototype.reset = function () {
this.nums = this.originNums.slice();
return this.nums;
};

/**
* @return {number[]}
*/
Solution.prototype.shuffle = function () {
const nums = this.nums,
length = nums.length;
for (let i = 0; i < length; i++) {
const random = Math.floor(Math.random() * (length - i)) + i;
[nums[i], nums[random]] = [nums[random], nums[i]];
}
return nums;
};

/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(nums)
* var param_1 = obj.reset()
* var param_2 = obj.shuffle()
*/
// @lc code=end

382. 链表随机节点

给你一个单链表,随机选择链表的一个节点,并返回相应的节点值。每个节点** 被选中的概率一样** 。
实现 Solution 类:

  • Solution(ListNode head) 使用整数数组初始化对象。
  • int getRandom() 从链表中随机选择一个节点并返回该节点的值。链表中所有节点被选中的概率相等。

image.png

示例:
输入 [“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 次

进阶:

  • 如果链表非常大且长度未知,该怎么处理?
  • 你能否在不使用额外空间的情况下解决此问题?

思路

蓄水池算法:
具体做法为:从前往后处理每个样本,每个样本成为答案的概率为 i/1 ,其中 i 为样本编号(编号从 1 开始),最终可以确保每个样本成为答案的概率均 1/n(其中 n 为样本总数)。

容易证明该做法的正确性,假设最终成为答案的样本编号为 k,那么 k 成为答案的充要条件为「在遍历到 k 时被选中」并且「遍历大于 k 的所有元素时,均没有被选择(没有覆盖 kk)」。

对应事件概率为:P = 1 / k _ (1 - 1/(k+1)) _ (1 - 1/(k+2)) _ … _ (1 - 1/n)

首项 1 / k 为选中 k 的概率,后面每项分别为编号为 [k + 1, n][k+1,n] 的样本 不被选中 的概率。

化简得:P = 1 / n

作者:AC_OIer
链接:https://leetcode-cn.com/problems/linked-list-random-node/solution/gong-shui-san-xie-xu-shui-chi-chou-yang-1lp9d/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

遍历一次链表,每一次指针指向,就随机生成一个数,如果小于 1/n 说明需要更新 target 到指针指向的 node,同时需要递增 n。

解答

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
34
35
36
37
38
39
40
41
42
43
/*
* @lc app=leetcode.cn id=384 lang=javascript
*
* [384] 打乱数组
*/

// @lc code=start
/**
* @param {number[]} nums
*/
var Solution = function (nums) {
this.nums = nums;
this.originNums = nums.slice();
};

/**
* @return {number[]}
*/
Solution.prototype.reset = function () {
this.nums = this.originNums.slice();
return this.nums;
};

/**
* @return {number[]}
*/
Solution.prototype.shuffle = function () {
const nums = this.nums,
length = nums.length;
for (let i = 0; i < length; i++) {
const random = Math.floor(Math.random() * (length - i)) + i;
[nums[i], nums[random]] = [nums[random], nums[i]];
}
return nums;
};

/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(nums)
* var param_1 = obj.reset()
* var param_2 = obj.shuffle()
*/
// @lc code=end