题目
题目来源:力扣(LeetCode)
设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。
insert(val):当元素 val 不存在时,向集合中插入该项。
remove(val):元素 val 存在时,从集合中移除该项。
getRandom:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。
示例 :
// 初始化一个空的集合。
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();
思路分析
数组 + 哈希表
数组存储值,在 gerRandom 时可以获得真正的随机值,哈希表存储值到索引的映射关系,可以在常数时间内获取到要删除元素的索引。
因此,我们使用以下的数据结构:
- 动态数组存储元素值
- 哈希表存储值到索引的映射
var RandomizedSet = function() {
// 存储值和索引的对应关系
this.map= new Map();
// 存储值
this.list = [];
};
insert 操作时
若元素不存在:
- 将元素添加到动态数组
- 在哈希表中添加值到索引的映射
- 最后返回 true
若元素已存在:
返回false
RandomizedSet.prototype.insert = function(val) {
if (!this.map.has(val)) return false;
this.list.push(val);
this.map.set(val, this.list.length - 1);
return true;
};
remove 操作时
若元素不存在:
- 返回false
若元素存在:
- 在哈希表中查找要删除元素的索引
- 将要删除元素与最后一个元素交换
- 删除最后一个元素
- 更新哈希表中的对应关系
- 最后返回 true ```javascript RandomizedSet.prototype.remove = function(val) { if (!this.map.has(val)) return false; // 在哈希表中查找要删除元素的索引 const index = this.map.get(val); const len = this.list.length - 1; // 将要删除元素与最后一个元素交换 [this.list[index], this.list[len]] = [this.list[len], this.list[index]]; // 更新哈希表中的对应关系 this.map.set(rhis.list[index], index); // 删除列表中的最后一个元素 this.list.pop(); // 删除哈希表中当前已删除元素的对应关系 this.map.delete(val); return true;
};
**getRandom 操作时**
借助 Math.random * list.length 与 0 做位运算,获取随机索引
```javascript
RandomizedSet.prototype.getRandom = function() {
// | 位运算符 OR, 一对数位执行位运算 OR 时,如果其中一位是 1 则返回 1
// 0 | 0 结果为 0
// 0 | 1 结果为 1
// 1 | 0 结果为 1
// 1 | 1 结果为 1
// Math.random() * this.list.length | 0 获取随机索引
return this.list[Math.random() * this.list.length | 0];
};
完整代码
/**
* Initialize your data structure here.
*/
var RandomizedSet = function() {
// 存储值和索引的对应关系
this.map= new Map();
// 存储值
this.list = [];
};
/**
* Inserts a value to the set. Returns true if the set did not already contain the specified element.
* @param {number} val
* @return {boolean}
*/
RandomizedSet.prototype.insert = function(val) {
// 元素已在集合中,返回 false
if (this.map.has(val)) return false;
// 向集合中插入元素,并返回 true
this.list.push(val);
this.map.set(val, this.list.length - 1);
return true;
};
/**
* Removes a value from the set. Returns true if the set contained the specified element.
* @param {number} val
* @return {boolean}
*/
RandomizedSet.prototype.remove = function(val) {
// 当前要删除的元素不在集合中,返回 false
if (!this.map.has(val)) return false;
// 在哈希表中查找要删除元素的索引
const index = this.map.get(val);
const len = this.list.length - 1;
// 将要删除元素与最后一个元素交换
[this.list[index], this.list[len]] = [this.list[len], this.list[index]];
// 更新哈希表中的对应关系
this.map.set(this.list[index], index);
// 删除列表中的最后一个元素
this.list.pop();
// 删除哈希表中当前已删除元素的对应关系
this.map.delete(val);
return true;
};
/**
* Get a random element from the set.
* @return {number}
*/
RandomizedSet.prototype.getRandom = function() {
// | 位运算符 OR, 一对数位执行位运算 OR 时,如果其中一位是 1 则返回 1
// 0 | 0 结果为 0
// 0 | 1 结果为 1
// 1 | 0 结果为 1
// 1 | 1 结果为 1
// Math.random() * this.list.length | 0 获取随机索引
return this.list[Math.random() * this.list.length | 0];
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* var obj = new RandomizedSet()
* var param_1 = obj.insert(val)
* var param_2 = obj.remove(val)
* var param_3 = obj.getRandom()
*/