JobHopper-InsertDeleteGetRandomO(1)

这题 很基础 很好!就是考察 各种数据结构 常见几种操作的复杂度 区分。101的 课上那张 表格应该浮现在眼前,hhh

这里说的 insert, delete 其实可以大致理解为 write, read

write & read 存在天生的 trade-off, 

最快的 write 就是 append, 但是会导致 read, search 时 需要遍历,而search 又是 insert, delete, update 这些操作的 前奏。

为了让read 更快,则需要在 write 时,准备好数据的排列 (tree structure),或者 创建 metadata 来得知 elem value 和 elem position 之间的关系,这就是 index 存在的意义。这就会导致 write 在时间和空间上需要更多 复杂度。

这题提出的要求是 write & read 的复杂度是 O(1) 这个 本身就是意味着 不可能是 只有一个 data structure. 为了达到 write O(1) 就 使用 array, 同时为了 delete O(1), 就用 dict 来记录 elem_value -> elem_position 的 metadata. 

还有一个要求就是 random select, 那就是说 elem 的存储 是 扁平的,抽签概率相同。

需要 练习,debug 才能 catch 到其中的 trick 和 易错地方,比如

!!在实现 remove 时,要对两个 data structure 进行修改

!!要 考虑到 elem_to_delete 和 elem_to_swap 其实是同一个 elem:  elem_to_delete is the last one in the array. 这就要求 pop() 在 set_value 之后,pop() 要在最后一步。

import random

class RandomizedSet:

    def __init__(self):
        self.dataset = []
        self.index = {}

    def insert(self, val: int) -> bool:
        if val not in self.index.keys():
            self.dataset.append(val)
            self.index[val] = len(self.dataset)-1
            return True
        else:
            return False

    def remove(self, val: int) -> bool:
        if val in self.index.keys():
            # 这个其实也可去掉了,因为 dataset, index 的最后操作是 pop()
            # if len(self.dataset) == 1:
            #     self.dataset=[]
            #     self.index = {}
            # else:
            del_idx = self.index[val]
            last_val = self.dataset[-1]

            # ops on dataset
            self.dataset[-1] = val
            self.dataset[del_idx] = last_val
            self.dataset.pop()

            # Bug: I forget to do anything in the index dict: 
            # (1) delete the {val, index} 
            # (2) set the swap -> idx in the map. Previously, it is swap -> len(self.data)-1
            # 以下两句不能互换!互换后,会出现 index out of bound error for: self.dataset[del_idx] = last_val 
            # 原因如下:
            # edge case: the val to delete is the last val in the array
            # pop() 在最后一步,是保证了 swap 其实没有发生时(del_val 本身就是最后一个elem),也能保证被removed
            self.index[last_val] = del_idx
            self.index.pop(val)
                
            
            return True
        else:
            return False

    def getRandom(self) -> int:
        randomIdx = random.randint(0, len(self.dataset)-1)
        return self.dataset[randomIdx]



def main():
    randSet = RandomizedSet()
    print(randSet.insert(1))
    print(randSet.insert(2))
    print(randSet.remove(2))
    print(randSet.insert(2))
    # expected
    # True
    # True
    # True
    # True

    # if change the order of index ops, the actual result will be the following, which is not correct
    # True
    # True
    # True
    # False


if __name__ == "__main__":
    main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值