天天看點

LeetCode 380: 常數時間插入、删除和擷取随機元素 Insert Delete GetRandom O(1)

題目:

設計一個支援在平均 時間複雜度 O(1) 下,執行以下操作的資料結構。

  1. insert(val)

    :當元素 val 不存在時,向集合中插入該項。
  2. remove(val)

    :元素 val 存在時,從集合中移除該項。
  3. getRandom

    :随機傳回現有集合中的一項。每個元素應該有相同的機率被傳回。

Design a data structure that supports all following operations in average O(1) time.

  1. insert(val)

    : Inserts an item val to the set if not already present.
  2. remove(val)

    : Removes an item val from the set if present.
  3. getRandom

    : Returns a random element from current set of elements. Each element must have the same probability of being returned.

示例 :

// 初始化一個空的集合。
RandomizedSet randomSet = new RandomizedSet();

// 向集合中插入 1 。傳回 true 表示 1 被成功地插入。
randomSet.insert(1);

// 傳回 false ,表示集合中不存在 2 。
randomSet.remove(2);

// 向集合中插入 2 。傳回 true 。集合現在包含 [1,2] 。
randomSet.insert(2);

// getRandom 應随機傳回 1 或 2 。
randomSet.getRandom();

// 從集合中移除 1 ,傳回 true 。集合現在包含 [2] 。
randomSet.remove(1);

// 2 已在集合中,是以傳回 false 。
randomSet.insert(2);

// 由于 2 是集合中唯一的數字,getRandom 總是傳回 2 。
randomSet.getRandom();           

解題思路:

  • 要求時間複雜度 O(1) 插入删除操作: 可以從零開始設計一個雜湊演算法, 也可以借助進階程式語言中已設計好的哈希集合/映射
  • 要求相同機率随随機傳回元素: 哈希集合無法做到随機傳回一個元素, 可以再借助一個順序存儲如數組, 随機産生索引下标, 傳回對應元素值

那麼就需要用哈希映射存儲元素, key 為元素值, value 為元素存儲在輔助數組中的索引下标值

插入操作就是數組, 哈希映射的插入操作

難點在于删除操作, 首先删除哈希映射中的該鍵值對, 其次删除數組中的該元素值, 不能簡單的通過賦一個不可能出現的數值僞删除, 因為這種僞删除會導緻數組越來越大撐爆記憶體。

代碼:

Java:

class RandomizedSet {
    List<Integer> list;
    Map<Integer, Integer> map;
    Random rand;

    /** Initialize your data structure here. */
    public RandomizedSet() {
        list = new ArrayList();
        map = new HashMap();
        rand = new Random();
    }

    /**
     * Inserts a value to the set. Returns true if the set did not already contain the specified element.
     * 正常插入操作
     */
    public boolean insert(int val) {
        if (map.containsKey(val))
            return false;
        map.put(val, list.size()); // value 表示存儲在 list 中的索引下标
        list.add(val); // 添加在數組 list 尾部
        return true;
    }

    /**
     * Removes a value from the set. Returns true if the set contained the specified element.
     * 
     */
    public boolean remove(int val) {
        if (!map.containsKey(val)) // 如果哈希映射中不存在該鍵 直接傳回 False
            return false;
        int tmp = list.get(list.size() - 1); // 暫存數組最後一位元素值
        int index = map.get(val); // 擷取待删除元素在 list 數組中對應的索引下标 index
        list.set(index, tmp); // 将 list 中該元素值改為暫存的數組最後一位值
        map.put(tmp, index); // 更新哈希映射中代表數組最後一位的鍵值對 對應的索引下标為 index
        list.remove(list.size() - 1); // 删除數組最後一位
        map.remove(val); // 删除哈希映射中該鍵值對
        return true;
    }

    /** Get a random element from the set. */
    public int getRandom() {
        return list.get(rand.nextInt(list.size())); // 産生一個大小為 [0, 數組大小) 的随機索引下标
    }
}           

Python:

from random import choice

class RandomizedSet:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.val_map = dict()
        self.val_list = list()

    def insert(self, val: int) -> bool:
        """
        Inserts a value to the set. Returns true if the set did not already contain the specified element.
        """
        if val not in self.val_map:
            self.val_map[val] = len(self.val_list) # value 表示存儲在 val_list 中的索引下标
            self.val_list.append(val) # 添加在數組 val_list 尾部
            return True
        return False

    def remove(self, val: int) -> bool:
        """
        Removes a value from the set. Returns true if the set contained the specified element.
        """
        if val in self.val_map:
            index = self.val_map[val] # 擷取待删除元素在 list 數組中對應的索引下标 index
            last_val = self.val_list[-1] # 暫存數組最後一位元素值
            self.val_list[index] = last_val # 将 list 中該元素值改為暫存的數組最後一位值
            self.val_map[last_val] = index # 更新哈希映射中代表數組最後一位的鍵值對 對應的索引下标為 index
            self.val_map.pop(val) # 删除哈希映射中該鍵值對
            self.val_list.pop() # 删除數組最後一位
            return True
        return False

    def getRandom(self) -> int:
        """
        Get a random element from the set.
        """
        return choice(self.val_list) # 傳回數組中的随機一個元素           

歡迎關注微_信公衆号: 愛寫Bug