384. 打乱数组
洗牌算法
class Solution:
def __init__(self, nums: List[int]):
self.nums = nums
self.original = nums.copy()
def reset(self) -> List[int]:
self.nums = self.original.copy()
return self.nums
def shuffle(self) -> List[int]:
random.shuffle(self.nums)
return self.nums
class Solution:
def __init__(self, nums: List[int]):
self.nums = nums
def reset(self) -> List[int]:
return self.nums
def shuffle(self) -> List[int]:
# tmp = self.nums.copy()
# n = len(tmp)
# for i in range(n):
# idx = random.randrange(i, n)
# tmp[i], tmp[idx] = tmp[idx], tmp[i]
# return tmp
# return random.sample(self.nums, len(self.nums))
tmp = self.nums.copy()
random.shuffle(tmp)
return tmp
519. 随机翻转矩阵
Leetcode
因为 m 和 n 最大能达到 10000,所以不能使用 O(m×n) 的空间复杂度来维护这个矩阵,这样会超出空间限制。
要尽量少的调用 random 产生随机数,保证每次 flip() 操作的时间复杂度尽可能低。
方法一:列表映射
将矩阵映射到 m × n 的一维列表, 映射函数 x = i ∗ n + j,将 (i, j) 映射到 x,x 对应 (x // n, x % n)。
使用字典 map 维护一个区间 [0, k],区间内对应的矩阵值始终为 0。 [0, k] 内的随机数 x 对应的 map 值与 k 对应的 map 值进行交换,然后 k -= 1。
class Solution:
def __init__(self, m: int, n: int):
self.m = m
self.n = n
self.total = m * n
self.map = {}
def flip(self) -> List[int]:
x = random.randrange(self.total)
self.total -= 1 # 注意区间 [0, self.total),先减一和随后减 1 的关系
# 查找 x 对应的映射
idx = self.map.get(x, x)
# 将 x 对应的映射设置为 total 对应的映射
self.map[x] = self.map.get(self.total, self.total)
return [idx // self.n, idx % self.n]
def reset(self) -> None:
self.total = self.m * self.n
self.map.clear()
方法二:找随机数两边没有用过的数
class Solution:
def __init__(self, m: int, n: int):
self.m = m
self.n = n
self.total = m * n
self.used = set()
def flip(self) -> List[int]:
a = b = random.randrange(self.total)
while a in self.used and a >= 0: a -= 1
while a == -1 and b in self.used and b < self.total: b += 1
x = a if a != -1 else b
self.used.add(x)
return [x // self.n, x % self.n]
def reset(self) -> None:
self.used.clear()
382. 链表随机节点
Leetcode
水塘抽样:从链表头开始,遍历整个链表,对遍历到的第 i 个节点,随机选择区间 [0,i) 内的一个整数,如果其等于 0,则将答案置为该节点值,否则答案不变。
class Solution:
def __init__(self, head: Optional[ListNode]):
# 方法一:列表
# self.arr = []
# while head:
# self.arr.append(head.val)
# head = head.next
# 方法二:抽样
self.head = head
def getRandom(self) -> int:
# return random.choice(self.arr)
node, res, i = self.head, 0, 1
while node:
if randrange(i) == 0:
res = node.val
node = node.next
i += 1
return res
398. 随机数索引
class Solution:
def __init__(self, nums: List[int]):
self.nums = nums
def pick(self, target: int) -> int:
# 方法一:Reservoir Sampling
# res, k = 0, 1
# for i, n in enumerate(self.nums):
# if n == target:
# if randrange(k) == 0:
# res = i
# k += 1
# return res
# 方法二:choice
return choice([i for i, n in enumerate(self.nums) if n == target])
本文介绍了LeetCode中的四个技术问题:打乱数组的洗牌算法、随机翻转矩阵的方法、链表随机节点抽取和随机数索引。探讨了高效实现策略,包括列表映射、查找未使用数和随机抽样等技巧。
255

被折叠的 条评论
为什么被折叠?



