小哆啦开始力扣每日一题的第十一天
《小哆啦解题记:实现一个名为 RandomizedSet 的类》
在编程王国里,有一位名叫小哆啦的年轻程序员。一天,他接到了一项挑战:实现一个名为 RandomizedSet 的类,这个类需要支持以下操作:
insert(val):如果元素val不在集合中,向集合中插入该元素并返回true,否则返回false。remove(val):如果元素val在集合中,删除该元素并返回true,否则返回false。getRandom():随机返回集合中的一个元素。
但是,还有一个额外的要求:每个操作的平均时间复杂度必须是 O(1)。
第一个尝试:O(n) 的方法
小哆啦开始思考,最直观的方法就是使用一个 数组 来存储所有的元素,然后实现相应的操作。
- 对于
insert,我们只需要将元素添加到数组末尾,并判断该元素是否已经存在,这个操作的时间复杂度是 O(n)。 - 对于
remove,如果我们直接遍历数组找到元素并删除它,时间复杂度也是 O(n)。 - 对于
getRandom,从数组中随机取一个元素可以在 O(1) 时间内完成。
小哆啦写下了以下代码:
class RandomizedSet {
private values: number[];
constructor() {
this.values = [];
}
insert(val: number): boolean {
if (this.values.includes(val)) {
return false; // 如果元素已存在,返回 false
}
this.values.push(val);
return true;
}
remove(val: number): boolean {
const index = this.values.indexOf(val);
if (index === -1) {
return false; // 如果元素不存在,返回 false
}
this.values.splice(index, 1); // 删除指定元素
return true;
}
getRandom(): number {
const randomIndex = Math.floor(Math.random() * this.values.length);
return this.values[randomIndex];
}
}
调试与分析
小哆啦很快就发现了问题:insert 和 remove 操作的时间复杂度竟然是 O(n)。特别是在进行删除时,数组中的元素需要移动,splice 操作会花费大量时间。而且,随着集合元素的增加,性能问题会变得越来越严重。
“这样下去不行!我需要找个更高效的解决方案。” 小哆啦自言自语。
优化方案:从 O(n) 到 O(1)
经过一番冥思苦想,小哆啦突然灵机一动——哈希表!如果使用一个哈希表来记录每个元素的索引,那么就可以在 O(1) 的时间内判断元素是否存在,并在 O(1) 时间内插入和删除元素。
为了进一步优化,数组仍然用来存储元素,哈希表的作用仅仅是提供快速查找和删除。
改进后的代码:
class RandomizedSet {
private valToIndex: Map<number, number>; // 值到索引的映射
private values: number[]; // 用于存储所有的值
constructor() {
this.valToIndex = new Map();
this.values = [];
}
// 插入元素
insert(val: number): boolean {
if (this.valToIndex.has(val)) {
return false; // 如果元素已存在,返回 false
}
// 插入新元素到数组末尾,并更新哈希表中的映射
this.valToIndex.set(val, this.values.length);
this.values.push(val);
return true;
}
// 删除元素
remove(val: number): boolean {
if (!this.valToIndex.has(val)) {
return false; // 如果元素不存在,返回 false
}
// 获取要删除元素的索引
const index = this.valToIndex.get(val)!;
// 获取数组中的最后一个元素
const lastVal = this.values[this.values.length - 1];
// 将最后一个元素移到要删除的元素位置
this.values[index] = lastVal;
this.valToIndex.set(lastVal, index);
// 删除数组中的最后一个元素
this.values.pop();
this.valToIndex.delete(val);
return true;
}
// 获取随机元素
getRandom(): number {
const randomIndex = Math.floor(Math.random() * this.values.length);
return this.values[randomIndex];
}
}
优化思路:
-
insert(val):- 使用哈希表
valToIndex来检查元素是否已经存在,查找时间复杂度是 O(1)。 - 将元素插入到
values数组末尾,插入操作 O(1)。
- 使用哈希表
-
remove(val):- 先通过哈希表快速查找元素的位置。
- 将数组中的最后一个元素移动到要删除的位置,保持数组连续性,时间复杂度 O(1)。
- 删除数组的最后一个元素,时间复杂度 O(1)。
-
getRandom():- 从
values数组中随机选择一个元素,时间复杂度 O(1)。
- 从
结果与总结
经过小哆啦的努力,他终于将 RandomizedSet 的操作时间复杂度从 O(n) 优化到 O(1)。通过巧妙地结合使用哈希表和数组,他成功地解决了这个挑战,成为了编程王国的英雄。
小哆啦对自己充满了信心:“编程的世界里,任何问题都有最优的解决方案。只要冷静分析,灵感总会到来!”