Leetcode random

本文介绍了LeetCode中的四个技术问题:打乱数组的洗牌算法、随机翻转矩阵的方法、链表随机节点抽取和随机数索引。探讨了高效实现策略,包括列表映射、查找未使用数和随机抽样等技巧。

384. 打乱数组

Leetcode

洗牌算法

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. 随机数索引

Leetcode

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])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值