哈希表

哈希表

概念:哈希表是一种数据结构,其使用哈希函数组织数据,以支持快速插入和搜索

原理:哈希表通过哈希函数将键映射到存储桶:

核心问题

链地址法实现

首先,设计哈希函数:\(hash(x) = x \ mod \ base\)

其中,base为哈希表的大小。开辟一个大小为base的数组,其每个位置是一个链表。对于输入\(x\),计算出\(hash(x)\),然后插入到对应位置的链表中。

base = 123
hash key       bucket
0         ->   123
1         ->   124 -> 2
2         ->   125 -> 248
...
122       ->   ... -> ...
// implementation of hash table using array + linked list
class MyHashSet{
private:
    static const int base = 123;
    vector<list<int>> data;
    int hash(int x){
        return x % base;
    }
public:

    MyHashSet() : data(base) {}

    void add(int key){
        int bucket = hash(key);
        if(contains(key))
            return;
        data[bucket].push_back(key);
    }

    bool contains(int key){
        int bucket = hash(key);
        for(auto it = data[bucket].begin(); it != data[bucket].end(); ++it){
            if((*it) == key)
                return true;
        }
        return false;
    }

    void remove(int key){
        int bucket = hash(key);
        for(auto it = data[bucket].begin(); it != data[bucket].end(); ++it){
            if((*it) == key)
                data[bucket].erase(it);
                return;
        }
    }
}
// when the contents are paired <key, value>
class MyHashMap {
private:
    static const int base = 123;
    vector<list<pair<int, int>>> data;
    int hash(int x){
        return x % base;
    }
public:
    /** Initialize your data structure here. */
    MyHashMap() : data(base) {}
    
    /** value will always be non-negative. */
    void put(int key, int value) {
        int bucket = hash(key);
        for(auto it = data[bucket].begin(); it != data[bucket].end(); ++it){
            if((*it).first == key){
                (*it).second = value;
                return;
            }
        }
        data[bucket].push_back(make_pair(key, value));
    }
    
    /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
    int get(int key) {
        int bucket = hash(key);
        for(auto it = data[bucket].begin(); it != data[bucket].end(); ++it){
            if((*it).first == key){
                return (*it).second;
            }
        }
        return -1;
    }
    
    /** Removes the mapping of the specified value key if this map contains a mapping for the key */
    void remove(int key) {
        int bucket = hash(key);
        for(auto it = data[bucket].begin(); it != data[bucket].end(); ++it){
            if((*it).first == key){
                data[bucket].erase(it);
                return;
            }
        }
    }
};

复杂度分析

若总共有\(M\)个键,则空间复杂度为\(O(M)\)。

通常情况下,插入和搜索的时间复杂度为\(O(1)\)。

最坏情况下,若桶大小的最大值为\(N\),则插入时间复杂度为\(O(1)\),搜索时间复杂度为\(O(N)\)。

内置哈希表原理

STL中的unordered_set

unordered_set是STL中hash set的实现

int main(){
    unordered_set<int> hashSet;
    hashSet.insert(3);
    hashSet.insert(4);
    hashSet.insert(5);

    hashSet.delete(3);

    if(hashSet.count(7) <= 0>){
        cout << "Key 7 is not in the hash set". << endl;
    }

    cout << "The size of hash set is : " << hashSet.size() << endl;

    for(auto it = hashSet.begin(); it != hashSet.end(); ++it){
        cout << (*it) << " ";
    }

    hashSet.clear(); // clear the hash set

    if(hashSet.empty()){
        cout << "Hash set is empty." << endl;
    }
}

应用

查重

bool findDuplicates(vector<int>& keys){
    unordered_set<int> hashSet;
    for(const int& key : keys){
        if(hashSet.count(key) > 0){
            return true;
        }
        hashSet.insert(key);
    }
    return false;
}