STL之hashtable

一、hashtable概述

hash table可提供对于任何有名项的存取和删除操作。由于操作对象是有名项,所以hashtable可以被视为一种字典结构。这种结构的用意在于提供常数时间的基本操作。
常用的hash函数有取余函数等,而解决hash冲突的方法有线性探测法、二次探测法、开链法。

线性探测法:当hash函数计算出元素的存放位置时,若该位置已有元素,则尝试将元素放在该元素的下一位置,若仍被占,继续尝试下一位置,即每次加1;

二次探测法:若计算出来元素的位置为h,若h以被占,则依序尝试h+12,h+22,h+32,h+42,h+52…….

开链法:为每个位置的元素维护一个链表,hash值相同的元素直接插入到链表末端。

SGI STL内的hashtable以开链法解决hash冲突,这里的“链”被称为桶,如下图所示:
这里写图片描述
bucket维护的linked list并非采用STL的list或slist,bucket聚合体以vector完成,以便有动态扩充能力。

二、hashtable内部介绍

hash table的节点定义如下:

template <class Value>
struct __hashtable_node
{
    __hashtable_node* next;
    Value val;
};

hashtable迭代器必须永远维系与整个buckets vector的关系,并记录当前所指的节点。其前进操作是首先尝试从目前所指的节点出发,前进一个位置,由于节点被安置在list内,所以利用节点的next指针即可达成前进操作。如果目前节点正巧是list的尾端,就跳至下一个bucket身上,那正是指向下一个bucket的头部节点。hashtable的迭代器没有后退操作,hashtable也没有定义所谓的逆向迭代器。

开链法不要求表格大小必须为质数,但是SGI STL仍然以质数来设计表格大小,并先将28个质数(逐渐呈现大约两倍的关系)计算好,以备随时访问。质数表如下{53,97,193,389,769,……},在设置表格大小时,找到大于或等于n的那个质数。

三、hashtable运用实例

目前STL中仅收录了unordered_set,unordered_map可供使用。hash_set,hash_map并未包含在STL库中,所以应尽量使用unordered_set,unordered_map来做hash处理。

// unordered_set示例程序
#include<unordered_set>
#include<iostream>
#include<ctime>
using namespace std;

void PrintfContainerElapseTime(char *pszContainerName, char *pszOperator, long lElapsetime)
{
    printf("%s 的 %s操作 用时 %d毫秒\n", pszContainerName, pszOperator, lElapsetime);
}

int main()
{
    const int MAXN = 50000, MAXQUERY = 5000;
    int a[MAXN], query[MAXQUERY];
    for (int i = 0; i < MAXN; ++i)
    {
        a[i] = (rand()*rand()) % MAXN;
    }
    for (int i = 0; i < MAXQUERY; ++i)
    {
        query[i] = (rand()*rand()) % MAXQUERY;
    }

    unordered_set<int> nhash_set;
    unordered_multiset<int> nhash_mset;
    nhash_set.insert(a, a + MAXN), nhash_mset.insert(a, a + MAXN);
    cout << "hash_set中有数据" << nhash_set.size() << "个" << endl;
    cout << "hash_multiset中有数据" << nhash_mset.size() << "个" << endl;

    int nFindSucceedCount, nFindFailedCount;
    nFindSucceedCount = nFindFailedCount = 0;
    clock_t clockBegin = clock();
    for (int i = 0; i < MAXQUERY; ++i)
    if (nhash_set.find(query[i]) != nhash_set.end())
        ++nFindSucceedCount;
    else
        ++nFindFailedCount;
    clock_t clockEnd = clock();
    PrintfContainerElapseTime("unordered_set", "find", clockEnd - clockBegin);
    printf("查询成功次数: %d    查询失败次数: %d\n", nFindSucceedCount, nFindFailedCount);

    nFindSucceedCount = nFindFailedCount = 0;
    clockBegin = clock();
    for (int i = 0; i < MAXQUERY; ++i)
    if (nhash_mset.find(query[i]) != nhash_mset.end())
        ++nFindSucceedCount;
    else
        ++nFindFailedCount;
    clockEnd = clock();
    PrintfContainerElapseTime("unordered_multiset", "find", clockEnd - clockBegin);
    printf("查询成功次数: %d    查询失败次数: %d\n", nFindSucceedCount, nFindFailedCount);

    return 0;
}

运行结果:
这里写图片描述

//unordered_map 示例程序
#include<unordered_map>
#include<iostream>
#include<ctime>
#include<string>
using namespace std;

void PrintfContainerElapseTime(char *pszContainerName, char *pszOperator, long lElapsetime)
{
    printf("%s 的 %s操作 用时 %d毫秒\n", pszContainerName, pszOperator, lElapsetime);
}

int main()
{
    const int MAXN = 50000, MAXQUERY = 5000;
    unordered_map<int, string> nhash_map;
    unordered_multimap<int, string> nhash_mmap;

    int query[MAXQUERY];
    for (int i = 0; i < MAXN; ++i)
    {
        nhash_map.insert(make_pair((rand()*rand()) % MAXN, "hello " + to_string((rand()*rand()) % MAXN)));
        nhash_mmap.insert(make_pair((rand()*rand()) % MAXN, "hello" + to_string((rand()*rand()) % MAXN)));
    }
    for (int i = 0; i < MAXQUERY; ++i)
    {
        query[i] = (rand()*rand()) % MAXQUERY;
    }


    cout << "unordered_map中有数据" << nhash_map.size() << "个" << endl;
    cout << "unordered_multimap中有数据" << nhash_mmap.size() << "个" << endl;

    int nFindSucceedCount, nFindFailedCount;
    nFindSucceedCount = nFindFailedCount = 0;
    clock_t clockBegin = clock();
    for (int i = 0; i < MAXQUERY; ++i)
    if (nhash_map.find(query[i]) != nhash_map.end())
        ++nFindSucceedCount;
    else
        ++nFindFailedCount;
    clock_t clockEnd = clock();
    PrintfContainerElapseTime("unordered_map", "find", clockEnd - clockBegin);
    printf("查询成功次数: %d    查询失败次数: %d\n", nFindSucceedCount, nFindFailedCount);

    nFindSucceedCount = nFindFailedCount = 0;
    clockBegin = clock();
    for (int i = 0; i < MAXQUERY; ++i)
    if (nhash_mmap.find(query[i]) != nhash_mmap.end())
        ++nFindSucceedCount;
    else
        ++nFindFailedCount;
    clockEnd = clock();
    PrintfContainerElapseTime("unordered_multimap", "find", clockEnd - clockBegin);
    printf("查询成功次数: %d    查询失败次数: %d\n", nFindSucceedCount, nFindFailedCount);

    return 0;
}

运行结果:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值