1.题目
题目地址(705. 设计哈希集合 - 力扣(LeetCode))
https://leetcode.cn/problems/design-hashset/
题目描述
不使用任何内建的哈希表库设计一个哈希集合(HashSet)。
实现 MyHashSet
类:
void add(key)
向哈希集合中插入值key
。bool contains(key)
返回哈希集合中是否存在这个值key
。void remove(key)
将给定值key
从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。
示例:
输入: ["MyHashSet", "add", "add", "contains", "contains", "add", "contains", "remove", "contains"] [[], [1], [2], [1], [3], [2], [2], [2], [2]] 输出: [null, null, null, true, false, null, true, null, false]解释: MyHashSet myHashSet = new MyHashSet(); myHashSet.add(1); // set = [1] myHashSet.add(2); // set = [1, 2] myHashSet.contains(1); // 返回 True myHashSet.contains(3); // 返回 False ,(未找到) myHashSet.add(2); // set = [1, 2] myHashSet.contains(2); // 返回 True myHashSet.remove(2); // set = [1] myHashSet.contains(2); // 返回 False ,(已移除)
提示:
0 <= key <= 106
- 最多调用
104
次add
、remove
和contains
2.题解
2.1 设计哈希映射
思路
这里主要解释下为何尽可能使用素数作为哈希集合/表的大小
参考:哈希表的大小为何最好是素数
参考:算法分析:哈希表的大小为何是素数
主要是因为很多数列都是间隔固定的周期性数列,存储这些数列时,发生哈希冲突的可能性和我们哈希集合的大小的因子数量成正比
如果待存储的数列间隔恰好是是被取模数的因子大小,那么合数要比素数更容易呈现周期性的取模重复。
代码
- 语言支持:C++
C++ Code:
class MyHashSet {
private:vector<list<int>> data;static const int base = 769; // 只有static const 变量才能在类内初始化, 只有static是不可的,需要类外初始化static int Hash(int key) { return key % base; }public:MyHashSet() : data(base) {}void add(int key) {int n = Hash(key);for (int num : data[n]) {if (num == key)return;}data[n].push_back(key);}void remove(int key) {int n = Hash(key);for (auto it = data[n].begin(); it != data[n].end(); it++) {if (*it == key) {data[n].erase(it);return; // 这里要注意,我们如果删除当前迭代器指向的对象后,不能再使用该迭代器, 否则会报 heap-use-after-free 的错误}}}bool contains(int key) {int n = Hash(key);for (int num : data[n]) {if (num == key)return true;}return false;}
};/*** Your MyHashSet object will be instantiated and called as such:* MyHashSet* obj = new MyHashSet();* obj->add(key);* obj->remove(key);* bool param_3 = obj->contains(key);*/
复杂度分析
令 n 为数组长度。
- 时间复杂度:\(O(n)\)
- 空间复杂度:\(O(n)\)