LeetCode 705: Design HashSet (Separate Chaining with Fixed Buckets)
LeetCode 705DesignHash TableToday we solve LeetCode 705 - Design HashSet.
Source: https://leetcode.com/problems/design-hashset/
English
Problem Summary
Design a HashSet without using built-in hash table libraries, supporting:
add(key), remove(key), and contains(key).
Key Insight
Use a fixed number of buckets and map each key to idx = key % bucketCount.
Each bucket stores keys that hash to the same index (separate chaining), so collisions are handled by bucket-local search.
Algorithm
1) Initialize an array of empty buckets.
2) add: locate bucket; append key only if not already present.
3) remove: locate bucket; delete key if found.
4) contains: scan bucket and return whether key exists.
Complexity Analysis
Average time per operation: O(1).
Worst-case per operation (all keys collide): O(n).
Space: O(n + B), where B is bucket count.
Reference Implementations (Java / Go / C++ / Python / JavaScript)
import java.util.*;
class MyHashSet {
private static final int SIZE = 2069;
private final List<Integer>[] buckets;
public MyHashSet() {
buckets = new ArrayList[SIZE];
for (int i = 0; i < SIZE; i++) buckets[i] = new ArrayList<>();
}
private int hash(int key) {
return key % SIZE;
}
public void add(int key) {
List<Integer> bucket = buckets[hash(key)];
for (int x : bucket) {
if (x == key) return;
}
bucket.add(key);
}
public void remove(int key) {
List<Integer> bucket = buckets[hash(key)];
for (int i = 0; i < bucket.size(); i++) {
if (bucket.get(i) == key) {
bucket.remove(i);
return;
}
}
}
public boolean contains(int key) {
List<Integer> bucket = buckets[hash(key)];
for (int x : bucket) {
if (x == key) return true;
}
return false;
}
}type MyHashSet struct {
buckets [][]int
}
const size = 2069
func Constructor() MyHashSet {
return MyHashSet{buckets: make([][]int, size)}
}
func (s *MyHashSet) hash(key int) int {
return key % size
}
func (s *MyHashSet) Add(key int) {
i := s.hash(key)
for _, x := range s.buckets[i] {
if x == key {
return
}
}
s.buckets[i] = append(s.buckets[i], key)
}
func (s *MyHashSet) Remove(key int) {
i := s.hash(key)
bucket := s.buckets[i]
for j, x := range bucket {
if x == key {
s.buckets[i] = append(bucket[:j], bucket[j+1:]...)
return
}
}
}
func (s *MyHashSet) Contains(key int) bool {
i := s.hash(key)
for _, x := range s.buckets[i] {
if x == key {
return true
}
}
return false
}class MyHashSet {
private:
static const int SIZE = 2069;
vector<vector<int>> buckets;
int hash(int key) {
return key % SIZE;
}
public:
MyHashSet() : buckets(SIZE) {}
void add(int key) {
auto& bucket = buckets[hash(key)];
for (int x : bucket) {
if (x == key) return;
}
bucket.push_back(key);
}
void remove(int key) {
auto& bucket = buckets[hash(key)];
for (int i = 0; i < (int)bucket.size(); i++) {
if (bucket[i] == key) {
bucket.erase(bucket.begin() + i);
return;
}
}
}
bool contains(int key) {
auto& bucket = buckets[hash(key)];
for (int x : bucket) {
if (x == key) return true;
}
return false;
}
};class MyHashSet:
SIZE = 2069
def __init__(self):
self.buckets = [[] for _ in range(self.SIZE)]
def _hash(self, key: int) -> int:
return key % self.SIZE
def add(self, key: int) -> None:
bucket = self.buckets[self._hash(key)]
if key not in bucket:
bucket.append(key)
def remove(self, key: int) -> None:
bucket = self.buckets[self._hash(key)]
for i, x in enumerate(bucket):
if x == key:
bucket.pop(i)
return
def contains(self, key: int) -> bool:
bucket = self.buckets[self._hash(key)]
return key in bucketvar MyHashSet = function() {
this.SIZE = 2069;
this.buckets = Array.from({ length: this.SIZE }, () => []);
};
MyHashSet.prototype.hash = function(key) {
return key % this.SIZE;
};
MyHashSet.prototype.add = function(key) {
const bucket = this.buckets[this.hash(key)];
for (const x of bucket) {
if (x === key) return;
}
bucket.push(key);
};
MyHashSet.prototype.remove = function(key) {
const bucket = this.buckets[this.hash(key)];
for (let i = 0; i < bucket.length; i++) {
if (bucket[i] === key) {
bucket.splice(i, 1);
return;
}
}
};
MyHashSet.prototype.contains = function(key) {
const bucket = this.buckets[this.hash(key)];
return bucket.includes(key);
};中文
题目概述
不使用内置哈希表库,实现一个 HashSet,支持:
add(key)、remove(key)、contains(key)。
核心思路
使用固定数量桶(bucket),通过 idx = key % bucketCount 把 key 映射到桶。
每个桶保存发生冲突的 key(拉链法),操作时只在线性扫描当前桶。
算法步骤
1)初始化桶数组。
2)add:在对应桶中查重,未出现才加入。
3)remove:在对应桶中找到并删除 key。
4)contains:扫描对应桶,判断 key 是否存在。
复杂度分析
平均每次操作时间复杂度:O(1)。
最坏情况(全部冲突):O(n)。
空间复杂度:O(n + B),B 为桶数量。
多语言参考实现(Java / Go / C++ / Python / JavaScript)
import java.util.*;
class MyHashSet {
private static final int SIZE = 2069;
private final List<Integer>[] buckets;
public MyHashSet() {
buckets = new ArrayList[SIZE];
for (int i = 0; i < SIZE; i++) buckets[i] = new ArrayList<>();
}
private int hash(int key) {
return key % SIZE;
}
public void add(int key) {
List<Integer> bucket = buckets[hash(key)];
for (int x : bucket) {
if (x == key) return;
}
bucket.add(key);
}
public void remove(int key) {
List<Integer> bucket = buckets[hash(key)];
for (int i = 0; i < bucket.size(); i++) {
if (bucket.get(i) == key) {
bucket.remove(i);
return;
}
}
}
public boolean contains(int key) {
List<Integer> bucket = buckets[hash(key)];
for (int x : bucket) {
if (x == key) return true;
}
return false;
}
}type MyHashSet struct {
buckets [][]int
}
const size = 2069
func Constructor() MyHashSet {
return MyHashSet{buckets: make([][]int, size)}
}
func (s *MyHashSet) hash(key int) int {
return key % size
}
func (s *MyHashSet) Add(key int) {
i := s.hash(key)
for _, x := range s.buckets[i] {
if x == key {
return
}
}
s.buckets[i] = append(s.buckets[i], key)
}
func (s *MyHashSet) Remove(key int) {
i := s.hash(key)
bucket := s.buckets[i]
for j, x := range bucket {
if x == key {
s.buckets[i] = append(bucket[:j], bucket[j+1:]...)
return
}
}
}
func (s *MyHashSet) Contains(key int) bool {
i := s.hash(key)
for _, x := range s.buckets[i] {
if x == key {
return true
}
}
return false
}class MyHashSet {
private:
static const int SIZE = 2069;
vector<vector<int>> buckets;
int hash(int key) {
return key % SIZE;
}
public:
MyHashSet() : buckets(SIZE) {}
void add(int key) {
auto& bucket = buckets[hash(key)];
for (int x : bucket) {
if (x == key) return;
}
bucket.push_back(key);
}
void remove(int key) {
auto& bucket = buckets[hash(key)];
for (int i = 0; i < (int)bucket.size(); i++) {
if (bucket[i] == key) {
bucket.erase(bucket.begin() + i);
return;
}
}
}
bool contains(int key) {
auto& bucket = buckets[hash(key)];
for (int x : bucket) {
if (x == key) return true;
}
return false;
}
};class MyHashSet:
SIZE = 2069
def __init__(self):
self.buckets = [[] for _ in range(self.SIZE)]
def _hash(self, key: int) -> int:
return key % self.SIZE
def add(self, key: int) -> None:
bucket = self.buckets[self._hash(key)]
if key not in bucket:
bucket.append(key)
def remove(self, key: int) -> None:
bucket = self.buckets[self._hash(key)]
for i, x in enumerate(bucket):
if x == key:
bucket.pop(i)
return
def contains(self, key: int) -> bool:
bucket = self.buckets[self._hash(key)]
return key in bucketvar MyHashSet = function() {
this.SIZE = 2069;
this.buckets = Array.from({ length: this.SIZE }, () => []);
};
MyHashSet.prototype.hash = function(key) {
return key % this.SIZE;
};
MyHashSet.prototype.add = function(key) {
const bucket = this.buckets[this.hash(key)];
for (const x of bucket) {
if (x === key) return;
}
bucket.push(key);
};
MyHashSet.prototype.remove = function(key) {
const bucket = this.buckets[this.hash(key)];
for (let i = 0; i < bucket.length; i++) {
if (bucket[i] === key) {
bucket.splice(i, 1);
return;
}
}
};
MyHashSet.prototype.contains = function(key) {
const bucket = this.buckets[this.hash(key)];
return bucket.includes(key);
};
Comments