散列哈希表
散列哈希表
散列技术是在记录的存储位置和他的关键字之间建立一个确定的对应关系f,
每个关键字key对应一个存储位置f(key)。查找时,根据这个对应的关系找到给定值key的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。我们把这种对应关系f称为散列函数,又称为哈希(Hash)函数。
映射函数叫做散列函数,
存放记录的数组叫做散列表。
散列函数的定义:
关键字key对应一个存储位置f(key)。
查找时,根据这个对应的关系找到给定值key的映射f(key),
若查找集合中存在这个记录,则必定在f(key)的位置上。
我们把这种对应关系f成为散列函数
1.如何进行值的存放:除留余数法
把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。
增加:
用数字本身模上哈希表的长度 即为这个数字在表上的存储位置
删除:
先找到它存储的位置,然后把这个存储的位置变成0
2.哈希冲突
冲突问题:两个不同的键映射到同一个位置
[例]
设关键词序列为 {47, 7, 29, 11, 9, 84, 54, 20, 30},
散列表表长TableSizeTableSize =13 (装填因子 αα= 9/13 ≈ 0.69),
散列函数为: Unexpected text node: ’ 'h(key)=keymod11。
用线性探测法处理冲突,列出依次插入后的散列表,并估算查找性能。
所以需要注意的是–余数最好用素数,不然很容易发生冲突 所以扩容不会二倍扩容,要用素数表进行扩容 合数会容易分布到集中的地方,质数则会分散均匀一些
{3,7,23,53…}
3.哈希冲突解决方法–线性检测法
发生冲突时,往定的位置的前后去存储,但是这样有问题就是 查找的复杂度会O(1)–>O(n)
并且要注意:
查找元素时遇到空桶 要对空桶进行区分–
1.这个桶没用过,则不需要再往后找
2.这个桶的元素被删除过,要继续向后找.(这样时间复杂度可能就会变成O(n))
4.哈希冲突解决方法–装载因子法
为了减少哈希冲突且时间复杂度会增多,哈希表有一个参数加载因子/装载因子
已经使用的桶的数量/桶的总数量>0.75 此时哈希表就要进行扩容
线性探测哈希表的实现代码
// 默认的计算哈希的实现类
template<typename K>
class default_hash
{
public:
int getHash(const K &key)
{
// 通用的计算key散列码的方式 用内存地址
int size = sizeof(K);
int sum = 0;
char *p = (char*)&key;
for (int i = 0; i < size; ++i)
{
sum += (p[i] << i); // h ello olleh
}
return sum;
}
};
#if 0
template<>
class default_hash<string>
//这是错误的计算散列码的方式 是用ASCII值来计算的
{
public:
int getHash(const string &key)
{
int hash = 0;
for (int i = 0; i < key.length(); ++i)
{
hash += (key[i] << i);
}
return hash;
}
};
#endif
template<typename K, typename Hash = default_hash<K>>
class CHashTable
{
public:
CHashTable(double loadFactor = 0.75)
:mpTable(new Node[mprimeTable[0]])
,mall_bucket_num(mprimeTable[0])
,muse_bucket_num(0)
,mloadFactor(loadFactor)
{
}
~CHashTable()
{
delete[]mpTable;
mpTable = nullptr;
}
void insert(const K &key) // 哈希表增加元素
{
double lf = muse_bucket_num * 1.0 / mall_bucket_num;
cout << "loadfactor:" << lf << endl;
if (lf > mloadFactor)
{
resize();
}
int index = mhash.getHash(key) % mall_bucket_num;
int index_stat = index;
do
{
if (USING != mpTable[index].mstate)
{
mpTable[index].mdata = key;
mpTable[index].mstate = USING;
muse_bucket_num++;
return;//报错用return 不要用break 避免公司其他人对代码进行添加
}
index = (index + 1) % mall_bucket_num;
} while (index_stat != index);
}
void erase(const K &key) // 哈希表删除元素
{
int index = mhash.getHash(key) % mall_bucket_num;
int index_stat = index;
do
{
if (UNUSE == mpTable[index].mstate)
{
return;
}
if (USING == mpTable[index].mstate
&& key == mpTable[index].mdata)
{
mpTable[index].mstate = USED; //析构
muse_bucket_num--;
return;
}
index