高级关联结构 --- 哈希表

来源:互联网 发布:mysql update in 批量 编辑:程序博客网 时间:2024/06/07 10:04

哈希表根据元素的值,把他们分布到各个簇中。哈希函数是一种关联函数,她把数据项映射到一个簇中,于是哈希表可以在该簇里进行添加,更新和删除数据的操作。与二叉搜索树一样,哈希表也可以用来实现集合和映射。但与二叉搜索树不同的是,哈希表的迭代器不能按元素的大小顺序访问他们。因此,用哈希表实现的集合与映射是无序的关联容器。

STL中没有哈希表类,也没有提供无序关联容器。

12.1 哈希表

哈希表:表中的每个元素都有唯一的键与之对应。这种关联结构的主要特征是哈希函数,她用键作为参数,并返回表中的某项的入口。可以把哈希表看做是一个有下标索引的数据序列,就像数组和向量一样。哈稀函数是个定位函数,她用键作为参数,返回表中的索引值。哈希的模型就是数组和向量的内存访问函数。哈希函数提供了另外一种不同的访问方式。她不是直接使用索引值,而是根据键求出一个索引,并用该索引来定位表中的某项数据。

12.2设计哈希函数:实际哈希函数不可能达到不产生冲突,应当能够产生均匀分布的哈希值。这样就可以使哈希表的索引散布在表中,从而尽可能的少发生冲突情况。

我们将哈希容器的声明中用到哈希函数,所以必须要有某种机制把哈希函数和容器类联系起来。C++语法允许一个函数把另外一个函数当做参数来用。这种方法效率很低,更好的途径是把函数当成一个函数对象类型的对象来处理。我们把哈希函数定义成函数对象,并扩充模板的语法,使之可以把函数对象类型作为模板参数,就像我们把普通数据类型传递给模板参数一样。

12.2.1 函数对象:

函数对象也就是类的对象,其行为类似于函数。但与普通函数不同的是,象其他类型的对象一样,可以创建,存储和销毁函数对象,他们也可以拥有相关的数据成员和操作。定义函数对象的第一步是声明一个类模板,其中包含了重载函数调用运算符()的成员函数。运算符()的参数表就是函数对象的参数表。

函数对象的类声明:

template<typename  T>

class functionObject

{public:

returnType operator()  (arguments)  const

{ //根据 参数计算返回值

return returnValue ;

}

 

} ;

程序员可以使用函数对象的匿名对象,只需在类型名称的后面加上运算符(),并在一对括号中列出参数表即可。

例如:greaterThan<int>()  ; //这个是greaterThan<int> 类型的匿名对象。

greaterThan<int>()  (x,y )  ;

  STL在头文件<functional>中定义了函数对象类型

greater 和less。

12.2.3 整型哈希函数:对于整型键,如果所有数字或者部分数字是随机出现的,那么恒等函数就是一个不错的哈希函数。我们用函数对象类型hFintID来实现恒等函数。运算符()用一个整数作为键,并返回与之对应的无符号整数值。

using namespace std;

class hFintID
{
public:
    unsigned int operator() (int item) const
    {
        return (unsigned int) item ;
    }
};

平方中值技术提供了一种哈希算法,她使用中间计算结果来产生随机值。该技术使用一个整型参数,把她转换为无符号整数,然后平方,从平方数的中间截取某几个连续位,作为哈希值返回。

class hFint
{
public:
    unsigned int operator() (int item )  const
    {
        unsigned int value = (unsigned int ) item ;
        value *= value ;
        value /= 256 ;   //舍低8位
        return value % 65536 ;  // 返回0--65535之间的整数
    }
};

12.2.4 字符串哈希函数

字符串 中的字符序列必须要联合起来构造一个无符号整数。我们同事使用乘法和加法来描述该算法。开始时,n=0 , 对于字符串中的每个字符,先用n*8,再加上该字符的值(ASCII码)。对字符串中的每个字符都进行了上述的乘法加法混合计算后,整数n的值可能为负,这是因为发生了整数溢出现象。这种情况下,只需对n取负号即可。用n去除一个很大的素数,返回其余数,这样做的效果是使整型哈希值的分布更加随机。这里,我们取的素数是2049 982 463  ;

class hFstring
{
public:
    unsigned int operator()  (const string& item) const
    {
        unsigned int prime = 2049982463 ;
        int n = 0 , i ;

        for (i = 0 ; i <item.length() ; i ++ )
        {
            n = n*8 +item[i] ;
        }

        return n > 0 ? (n % prime) : (-n % prime) ;
    }
};

12.2.5 实数哈希函数

class hFreal
{
public:
    unsigned int operator()(double item) const
    {
        int exp;
        double mant;
        unsigned int hashval;
        if (item == 0)
            hashval = 0;
        else
        {
            mant = frexp(item,&exp);
            hashval = (unsigned int)((2 * fabs(mant) -1) * (unsigned int)~0);
        }
        return hashval;
    }
};

12.3.2  独立表链地址法

把哈希表定义成容器(如向量和链表)的索引序列。每个容器(称作桶),用来保存映射到同一位置上的数据集合。这种解决冲突的方法叫做独立表链地址法。

假设每个桶都是一个表。哈希表就是一个包含list对象的向量。

12.4 哈希类(hash)类

hash类有两个模板参数,而stree类只有一个模板参数。由于表的大小对构建hash对象是必须的,所以构造函数需要把表长(桶的个数)作为他的一个参数。

哈希迭代器有3个部分组成:指向哈希表的指针,当前桶的索引和元素在桶中的list迭代器 。