1. 程式人生 > 其它 >演算法學習筆記(三)——散列表

演算法學習筆記(三)——散列表

1.1 散列表(雜湊表)

  散列表(雜湊表)是根據鍵值而直接進行訪問的資料結構。通過一個雜湊函式將查詢的鍵轉換為表中的一個索引,來加快查詢的速度。理想情況下,不同的鍵值都能轉化為不同的索引值,但是在現實中,我們常常要處理多個鍵值對應同一個索引值。所以,雜湊查詢的演算法分為兩個部分。第一部分就是關鍵的雜湊函式,第二部分就是處理雜湊衝突。

1.2 雜湊函式

  雜湊函式就是通過該函式將元素的鍵值轉換為表的索引。雜湊函式應該易於計算並且能夠均勻分佈所有的鍵,得到的雜湊值應該是一個非負整數,每個不同的鍵都有一一對應的雜湊值。

1.3 雜湊衝突的處理

  1. 拉鍊法

    將表的每一個索引位置都對應一個連結串列,連結串列的每個結點都儲存了對應的鍵值。

  2. 開放地址法

    用大小為M的陣列儲存N個鍵值對(M>N),通過依靠陣列中的空位解決雜湊衝突。最簡單的開放地址法便是線性探測法,當碰撞發生時,我們直接檢查散列表的下一個位置。此時可能有三種結果,命中,未命中或者繼續查詢。

2.程式碼實現

  1. 基於拉鍊法實現的散列表

    template <typename key, typename value>
    class ChainingListHashTable
    {
    private:
        int N;      //鍵值對數量
        int M;      //散列表的大小
        vector<vector<value>> List;
    
        int MyHash(key key)
        {
            hash<key> hash_key;
            return (hash_key(key) & 0x7ffffff % M);
        }
    public:
        ChainingListHashTable(int m)
        {
            N = 0;
            M = m;
            List.resize(m);
        }
        
        value get(key key)
        {
            return List[MyHash(key)][0];
        }
    
        void put(key key, value value)
        {
            List[MyHash(key)].push_back(value);
            N++;
        }
        
        void erase(key key)
        {
            value v = MyHash(key);
            for(int i = 0; i < List[v].size(); ++i)
            {
                if(List[v][i]==key)
                {
                    List[v].erase(List[v].begin()+i);
                    return;
                }    
            }
            cerr<<"No such key in List"<<endl;
            return;
        }
    };
    
  2. 基於線性探測法的散列表

    template <typename key, typename value>
    class LinearProbingHashTable
    {
    private:
        int N;
        static int M = 16;
        vector<key> keys;
        vector<value> values;
        int MyHash(key key)
        {
            hash<key> hash_key;
            return (hash_key(key) & 0x7ffffff % M);
        }
        void resize(int cap)
        {
            keys.resize(cap);
            values.resize(cap);
        }
    
        bool contains(key key)
        {
            for (int i = MyHash(key); keys[i] != NULL; i = (i + 1) % M)
            {
                if (keys[i] == key)
                    eturn true;
            }
            return false;
        }
    
    public:
        LinearProbingHashTable()
        {
            keys.resize(M);
            values.resize(M);
        }
    
        void put(key key, value value)
        {
            if (N >= M / 2)
                resize(2 * M);
            int i;
            for (i = MyHash(key); keys[i] != NULL; i = (i + 1) % M)
            {
                if (keys[i] == key)
                {
                    values[i] = value;
                    return;
                }
            }
            keys[i] = key;
            values[i] = value;
            N++;
        }
    
        value get(key key)
        {
            for (int i = MyHash(key); keys[i] != NULL; i = (i + 1) % M)
            {
                if (keys[i] == key)
                    return values[i];
            }
            return NULL;
        }
    
        void erase(key Key)
        {
            if (!contains(Key))
                return;
            int i = MyHash(Key);
            while (keys[i] != Key)
                i = (i + 1) % M;
            keys[i] = NULL;
            values[i] = NULL;
            i = (i + 1) % M;
            while (keys[i] != NULL)
            {
                key keyToRedo = keys[i];
                value valueToRedo = values[i];
                keys[i] = NULL;
                values[i] = NULL;
                N--;
                put(keyToRedo, valueToRedo);
                i = (i + 1) % M;
            }
            if (N > 0 && N == M / 8)
                resize(M / 2);
        }
    };