概述
set是ES6当中一个新的数据结构,set类似于数组,内部元素可以是基础类型,或者是对象,但是必须是唯一的。
对于set,如果想模拟实现它的add、delete、has方法,可以使用一个内部array来保存数据:indexOf来返回是否has某个元素;add新元素的时候使用indexOf查找元素是否已经存在,再决定是否插入;delete的时候,也是使用indexOf来找到元素的index,然后使用splice方法删除。
不过indexOf的时间复杂度是O(n)。那么有没有O(1)时间复杂度的模拟方法呢?
最近发现leetcode有道380. Insert Delete GetRandom O(1)。这不就是模拟了set的add、delete、has么?
题目阅读
首先看leetcode这道题的题目:
Design a data structure that supports all following operations in average O(1) time.
insert(val): Inserts an item val to the set if not already present.
remove(val): Removes an item val from the set if present.
getRandom: Returns a random element from current set of elements.
Each element must have the same probability of being returned.
Example:
// Init an empty set. RandomizedSet randomSet = new RandomizedSet();
// Inserts 1 to the set. Returns true as 1 was inserted successfully. randomSet.insert(1);
// Returns false as 2 does not exist in the set. randomSet.remove(2);
// Inserts 2 to the set, returns true. Set now contains [1,2]. randomSet.insert(2);
// getRandom should return either 1 or 2 randomly. randomSet.getRandom();
// Removes 1 from the set, returns true. Set now contains [2]. randomSet.remove(1);
// 2 was already in the set, so return false. randomSet.insert(2);
// Since 2 is the only number in the set, getRandom always return 2. randomSet.getRandom();
要求常数时间内插入、删除和获取随机数。前两个要求不就是set的add和delete么?这道题的实现方法很有趣,因此,拿出来和大家分享一下。
那我们看看这道题的解法是什么:
解题
首先是这个set的初始化,我们要使用一个数组this.set来保存插入set的元素,然后,关键来了,我们还需要一个Map类型的变量来保存数组每个元素的index。
/**
* Initialize your data structure here.
*/
var RandomizedSet = function() {
this.set = [];
this.map = new Map();
};
然后,实现insert这个方法。和使用indexOf的方法很类似,我们先要找到这个元素是否已经存在于this.set这个数组中。但是,我们不使用indexOf方法,而是在this.map中查找这个元素是否已经存在:
if (this.map.get(val) === undefined) return false;
如果不存在,那我们就需要把这个元素push到this.set当中,然后也需要把这个元素和其index放到this.map当中,最后insert代码如下所示:
/**
* 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) {
if (this.map.get(val) !== undefined) return false;
this.set.push(val);
this.map.set(val, this.set.length-1);
return true;
};
第三个要求是实现delete方法,这也是这道题目最tricky的地方。
首先,还是需要判断元素是否存在this.set当中,删除一个不存在的元素是没有任何意义的,具体做法和上面insert相同,不再赘述。
之后,我们要找到需要删除的元素的位置,那么,如果不使用indexOf,我们怎么知道这个元素的位置呢?聪明的朋友已经想到了,this.map不就保存着这个元素的位置么?问题又来了,把这个元素直接删掉了之后,那它后面的元素的位置不都依次减1了么?
trick就在这里:我们用最后一个元素,替换掉需要删掉的元素,然后,把最后一个元素的index更改为被删掉的元素的index,最后,把最后那个元素删掉,这样,后面的元素的位置就不会依次减一。
代码如下:
/**
* 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) {
if (this.map.get(val) === undefined) return false;
// 把最后一个元素填充到被删掉元素的位置
let last = this.set[this.set.length-1];
this.set.splice(this.map.get(val), 1, last);
// 把最后一个元素删掉
this.set.pop();
// 把最后一个元素的index改掉
this.map.set(last, this.map.get(val));
this.map.delete(val);
return true;
};
getRandom跟set关系不大,不再详述。
最终的代码如下:
/**
* Initialize your data structure here.
*/
var RandomizedSet = function() {
this.set = [];
this.map = new Map();
};
/**
* 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) {
if (this.map.get(val) !== undefined) return false;
this.set.push(val);
this.map.set(val, this.set.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) {
if (this.map.get(val) === undefined) return false;
let last = this.set[this.set.length-1];
this.set.splice(this.map.get(val), 1, last);
this.set.pop();
this.map.set(last, this.map.get(val));
this.map.delete(val);
return true;
};
/**
* Get a random element from the set.
* @return {number}
*/
RandomizedSet.prototype.getRandom = function() {
let length = this.set.length;
let index = Math.floor(Math.random() * Math.floor(length));
return this.set[index];
};
/**
* 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()
*/
小结
小结一下吧,这个方法好么?当然好啦,时间复杂度刷刷降为O(1)。那这个方法不好么?当然也不好,空间复杂度上去了,因为我们需要this.map来存储数据,而且Map是ES6的方法,为了实现ES6中的set,使用另一个ES6的方法,这显得不是很妥当。不过这道题给我们提供了一个空间换时间的方法,还是很值得和大家分享的。最后,祝大家前端学习一切顺利。
关注我的公众号:前端三剑客
![661e215f3bf22a759af7317f91daa80f.png](https://file2.kaopuke.com:8081/files_image/2023061122/661e215f3bf22a759af7317f91daa80f.png)
最后
以上就是清脆网络为你收集整理的oracale delete 返回-2_算法面试题——set常数时间内实现add、delete、has的全部内容,希望文章能够帮你解决oracale delete 返回-2_算法面试题——set常数时间内实现add、delete、has所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复