hash 关联容器

1 hashtable

    输入数据 `足够随机`
        RB-tree `对数`平均时间
    else 
        hashtable: `常数`平均时间, 基于 `统计`
16bit排列组合的非负整数序列 0-65535
    
                        插 i: a[i]++
                      / 
    `常数` 平均时间     - 删 i: a[i]--
        |             \
        |               找 i: if(a[i] == 0)
        | 2个代价                  
        |/            2个问题  |- 32bit => 元素数 2^32 = 4G, 数组太大 <- - - - - - - - -
 - -[1] `大数组` 存储 - - - - -                                                      |
|                              |                                                        |
|   [2] 初始化为 0             |- 元素不是整数, 如 `字符串` -> 索引(index) 如何表示 ?   |
|                                                                                       |
|                                   整数: 整数编码 -> 字符: 字符编码为整数                 |   
|                                                                                       |
|                                       "jjhou" = 'j'*128^4 + ... + 'u'*128^0 = 大数 - - 
|
| 解决
|- - -> hashFunc(哈希函数): 将 `大数(整数/字符串的编码值) 映射为 小数` -> 作大小可接受的数组 `索引(index)`
            |
            | 问题
            |/
        哈希冲突: `不同元素映射到相同位置(相同索引)`
            |
            | 解决 
            |/
        开链法 
            
            buckets vector + bucket(桶) list                                                                                                                                                                                                                                                   ) list 
            
    
    template<class Value>
    struct __hashtable_node
    {
        __hashtable_node* next;
        Value             val;
    }

    template<class Value, class Key, class HashFcn,
            class ExtractKey, class EqualKey, calss Alloc>
    struct __hashtable_iterator
    {
        // 2个成员数据
        node* cur;      // [1] 指向 node  
        hashtable* ht;  // [2] 相应的 ht: 可能要从 curBucket 跳到 nextNotEmptyBucket
        
        iterator 
        operator++()
        {
            const node* old = cur;
            
            cur = cur->next;
            
            if( !cur ) 
            {
                // [1] 元素值 -> hashFunc -> index
                size_type bucketIndex = ht->bkt_num(old->val); 
                
                // [2] 逐个bucket 遍历, 直到到达1个 `非空 bucket`
                while( !cur && ++bucketIndex < ht->buckets.size() )
                    cur = ht->buckets[bucketIndex];
            }
            
            return *this;
        }
    }
    
    template<class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, calss Alloc>
    class hashtable
    {
        // ...
        
    private:
        HashFcn                 hash;
        ExtractKey              get_key;
        EqualKey                equals;
        vector<node*, Alloc>    buckets; // 中控表
        size_type               num_elements;
    public:
        hashtable(size_type n, const HashFcn& hash_, const EqualKey& equal_)
            : hash(hash_), get_key(ExtractKey() ), equals(equal_), num_elements(0)
        {
            initialize_buckets(n);
        }
    };
    
        是 node 中 dateType, 不是 key-value 中的 Value 的类型
        |\          
        |             key 即 value
        |   hash_set: - - - - - - -hash_set::Value
        |  /
    Value   
        |  \            
        |   hash_map: - - -        hash_map::pair<const Key, T>
        |   
        |/  
    ExtractKey  
    |
    | 据 Value(node 的 data) 获取 key: hash_table 内部用, 插入  
    |/
    Key // 第2参数
      
    EqualKey
        据 `key 相等函数(对象)` 判2个 key 是否相等, 对 hash_set/map 相等不插入 
            if(equals(get_key(node->val), get_key(node->val) ) )
            
    HashFcn // 第3参数
        据 key 得 bucketIndex 
            hash(key) % buckets.bucketNum

hash functions

Note: 自定义类型, 用户必须写 hashFunc, 否则 hashtable 无法处理

<stl_hash_fun.h>
    
    // 主模板 
    template <class Key>
    struct hash {};
    
    // 辅助函数 
    inline size_t 
    __stl_hash_string(const char* s)
    {
        unsigned long h = 0;
        for(; *s; ++s)
            h = 5*h + *s;
    }
    
    // 特化
    struct hash<char*>
    {
        size_t operator()(const char* s) const 
        {
            return __stl_hash_string(s);
        }
    };
    
    // 
    struct hash<int>
    {
        size_t operator()(int x) const 
        {
            return x;
        }
    };

2 hash_set: 模板形参中不必指定 Key, ExtractKey: std::identity

template <class Value, 
          class HashFcn = std::hash<Value>,
          class EqualKey = std::equal_to<Value>,
          class Alloc = alloc>
class hash_set
{
private:
    typedef hashtable<Value, Value, HashFcn, 
                     std::identity<Value>, EqualKey, Alloc> ht;
                     
    ht rep;   // 底层以 hash_table 完成
public:
    hash_set():
        rep(100, hasher(), key_equal() ) {}
}  

Note: 新的 std::identity 不再是模板类

3 hash_map: 模板形参中不必指定 ExtractKey: std::select1st<pair<const Key, T> >, pair<const Key, T> 为 hashtable 中的 Value

template <class Key, class T, 
          class HashFcn = std::hash<Key>,
          class EqualKey = std::equal_to<Key>,
          class Alloc = alloc>
class hash_map
{
private:
    typedef hashtable<std::pair<const Key, T>, T, HashFcn, 
                     std::select1st<std::pair<const Key, T> >, EqualKey, Alloc> ht;
                     
    ht rep;   // 底层以 hash_table 完成
public:
    hash_set():
        rep(100, hasher(), key_equal() ) {}
}  

Note: 新的 std::select1st 更名为 std::_Select1st

4 hash_multiset

5 hash_multimap

Note1: EqualKey 为什么不直接用 strcmp ?

答: EqualKey 要求的是 ture / false,而 strcmp 返回 -1,0,1, 所以必须加一层 封装

eg1: 想保留50个 节点, 最接近 50 的质数是 53(hashFunc 对 53 取余), 所以保留 53 个 buckets

... ht(50, hash<int>(), std::equal_to<int>() )

    
    #0      #1      #2                  #52
    —————————————————————————————————
    |     |     |       |   
    —|———————————————|————————————————
     |               |
    _|/_            _|/_     _ _        
    |  —| ->|       |  —|-->|  —|->|    
    |_ _|           |_ _|   |_ _|   
    |   |           |   |   |   |       
    | 53|           |108|   | 2 |   
    |_ _|           |_ _|   |_ _|   
               
    #include <hash_set>     // for hashtable, Note: client 不能直接 include <stl_hashtable.h>

    #include <functional>   // std::identity

    #include <cstring>      // strcmp 
    #include <iostream>

    struct EqStr
    {
        bool 
        operator()(const char* s1, const char* s2) const
        {
            return strcmp(s1, s2) == 0;
        }
    };

    int main()
    {
        __gnu_cxx::hashtable<const char*, const char*, std::hash<const char*>,
            std::identity, EqStr>
            ht(50, std::hash<const char*>(), EqStr());// 可不带参数, 默认 Ctora: get_key(ExtractKey() )

        ht.insert_unique("xianyang");
        ht.insert_unique("xian");
        ht.insert_unique("beijing");

        __gnu_cxx::hashtable<const char*, const char*, std::hash<const char*>,
            std::identity, EqStr>::iterator beg, end;

        beg = ht.begin();
        end = ht.end();

        for (; beg != end; ++beg)
            std::cout << *beg << " ";
        std::cout << std::endl;
    }

// output:
xianyang xian beijing
    #include <hash_set>     

    #include <cstring>     
    #include <iostream>

    struct EqStr
    {
        bool 
        operator()(const char* s1, const char* s2) const
        {
            return strcmp(s1, s2) == 0;
        }
    };

    int main()
    {
        __gnu_cxx::hash_set<const char*, std::hash<const char*>, EqStr>
            hSet;

        hSet.insert("xianyang");
        hSet.insert("xian");
        hSet.insert("beijing");

        __gnu_cxx::hash_set<const char*, std::hash<const char*>, EqStr>::iterator beg, end;

        beg = hSet.begin();
        end = hSet.end();

        for (; beg != end; ++beg)
            std::cout << *beg << " ";
        std::cout << std::endl;
    }

    // output:
    xianyang xian beijing
    #include <hash_map>     

    #include <cstring>     
    #include <iostream>

    struct EqStr
    {
        bool 
        operator()(const char* s1, const char* s2) const
        {
            return strcmp(s1, s2) == 0;
        }
    };

    int main()
    {
        __gnu_cxx::hash_map<const char*, int, std::hash<const char*>, EqStr> hMap;

        hMap["xianyang"] = 1;
        hMap["xian"] = 2;
        hMap["beijing"] = 3;

            __gnu_cxx::hash_map<const char*, int, std::hash<const char*>, EqStr>::iterator beg, end;

        beg = hMap.begin();
        end = hMap.end();

        for (; beg != end; ++beg)
            std::cout << beg->first << " ";
        std::cout << std::endl;
    }
    // output:
    xianyang xian beijing
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容