【每日算法】O(1)时间插入、删除和获取随机元素

135 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

O(1)时间插入、删除和获取随机元素

实现RandomizedSet 类:

  • RandomizedSet() 初始化 RandomizedSet 对象
  • bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
  • bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
  • int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
  • 你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1) 。

思路:

  • 题目要求所有函数的时间复杂度都是 O(1),可以想到使用哈希表实现

  • 要维护当前存在的所有数,可以考虑使用数组来维护。但是用数组维护的话就存在一个问题、

    • 数组在最后面加入一个数的时间复杂度是 O(1)

    • 数组在最后面删除一个数的时间复杂度是 O(1),但是数组在删除中间的元素时,时间复杂度是 O(n)

      • 这是因为数组每次删除中间的元素,都会对其后面的全部元素进行位置的调整
  • 我们可以考虑使用哈希表维护每个数加入时的坐标

  • 在要删除的数不是数组的最后一个元素时,让其与最后一个元素进行交换(因为是不在乎顺序的,所以任意交换都不影响)

  • 此时要删除的数成为数组的最后一个元素,就可以用 O(1) 的时间复杂度删掉了

 var RandomizedSet = function() {
   this.map = new Map()
   this.nums = new Array()
 }
 RandomizedSet.prototype.insert = function(val) {
   if(!this.map.has(val)) { // 当前值不存在哈希表中,才执行插入操作
     this.map.set(val, this.nums.length) // 将val当作键,其在数组中存放的位置当作值
     this.nums.push(val)
     return true
   }
   return false
 }
 RandomizedSet.prototype.remove = function(val) {
   if(this.map.has(val)) { // 当值存在于哈希表中,才执行删除操作
     const swapVal = this.nums[this.nums.length - 1] // 获取数组中的最后一个元素的值
     const idx = this.map.get(val) // 获取当前元素在数组中的位置
     // 将数组最后一个元素的值,赋值给当前元素的位置
     // 例如:[1,2,3,4,5],假设要删除 2,则 赋值过后此时数组为:[1,5,3,4,5]
     this.nums[idx] = swapVal 
     this.map.set(swapVal, idx) // 更新 “5” 在哈希表中的值
     this.map.delete(val) // 删除哈希表中的 “2”
     this.nums.pop() // 删除数组的最后一个元素,示例数组现在为:[1,5,3,4]
     return true
   }
   return false
 }
 RandomizedSet.prototype.getRandom = function() {
   const randomIdx = Math.floor(Math.random() * this.nums.length)
   return this.nums[randomIdx]
 }

\