LeetCode 380: 常數時間插入、刪除和獲取隨機元素 Insert Delete GetRandom O(1)

  • 2019 年 12 月 16 日
  • 筆記

題目:

設計一個支援在平均 時間複雜度 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) # 返回數組中的隨機一個元素