HashTable——哈希表(散列表)
程序员文章站
2024-02-29 23:35:40
...
哈希表:
哈希表/散列表 :是根据关键字(key)而访问在内存位置的数据结构。
其方法是 :它通过一个关键值的函数将所需的数据映射到表中的位置来访问数据,这个映射函数叫做散列函数,存放记录的数组叫做散列表(哈希表)。
构造哈希表的几种方法:
- 直接定址法 —取关键字的某个线性函数为散列地址,Hash(key) = key 或 Hash(key) = i key+ j;其中i,j为常数。
- 除留余数法 —取关键值被某个不大于散列表长m的数p除后所得的余数为散列地址。Hash(key) = key%p.
- 平方取中法
- 折叠法
- 随机数法
- 数学分析法
(其中 直接定址法和 除留余数法是常用的方法)
哈希碰撞/哈希冲突 :
不同的key值经过哈希函数Hash(key)处理以后,可能得到相同的地址。而任意的哈希函数都不能避免这种情况的发生(哈希碰撞/哈希冲突)。
其中散列表的载荷因子定义为:a = 填入表中的元素个数 / 散列表的个数 (eg: index = _size /_tables.size());即a越大,散列表中的数越多。
开放定址法 —处理哈希冲突的闭散列方法:
对于开放定址法来说,散列表的载荷因子是特别重要的因素。应该严格控制到0.7-0.8以下!所以这也是我们需要扩容的条件 ( _size*10 / _tables.size()*10 > 7 || _tables.size() == 0的时候扩容)
方法包括:
线性探测:
二次探测:
完整代码(线性探测):
- HashTable.h
- test.cpp
Hashtable.h
//数的状态
enum state
{
EXIST,//存在
EMPTY,//空
DELETE,//删除
};
template<class K,class V>
struct HashNode
{
K _key;
V _value;
state _state;
HashNode(const K& key = K(),const V& value = V())
:_key(key)
,_value(value)
,_state(EMPTY)
{}
};
template <class K>//仿函数
struct Hash
{
size_t operator()(const K& key)
{
return key;
}
};
template <>
struct Hash<string>
{
static size_t BKDRHash(const char* str)
{
unsigned int seed = 131;
unsigned int hash = 0;
while(*str)
{
hash = hash*seed+(*str++);
}
return (hash& 0x7FFFFFFF);
}
size_t operator()(const string& str)
{
return BKDRHash(str.c_str());
}
};
template <class K,class V,class _HashFunc = Hash<K>>
class HashTable
{
typedef HashNode<K,V> Node;
public:
HashTable()
:_size(0)
{}
bool Insert(const K& key,const V& value)
{
//1.查找key,存在的话,返回false
//2.检查内存
//3.添加key
CheckCapacity();
if(Find(key))
return false;
size_t index = HashFunc(key);
while(_table[index]._state == EXIST)//状态一直为存在的时候一直往下找,直到找到下一个为EMPTY或者DELETE的位置
{
++index;
if( index == _table.size())
{
index = 0;
}
}
_table[index]._key = key;
_table[index]._value = value;
_table[index]._state = EXIST;
_size++;
return true;
}
//查找K
Node* Find(const K& key)
{
size_t index = HashFunc(key);
while( _table[index]._state != EMPTY )
{
if(_table[index]._key == key )
{
if(_table[index]._state == EXIST)
{
return &_table[index];
}
else
return NULL;
}
++index;
if( index == _table.size() )
{
index = 0;
}
}
return NULL;
}
//扩容
void CheckCapacity()
{
//扩容条件:
//1.载荷因子 a>0.7
//2.哈希表为空
if(_table.size() == 0
|| _size*10 / _table.size() >= 7)
{
size_t newsize = _table.size()*2;
if(newsize == 0)
{
newsize = 10;
}//初始空间为空的时候,开辟空间为10
HashTable<K,V,_HashFunc> newht;
newht._table.resize(newsize);
for(size_t i = 0; i < _table.size(); ++i)
{
//将原来哈希表存在的数存入(一一映射)新的哈希表中
if(_table[i]._state == EXIST)
{
newht.Insert (_table[i]._key,_table[i]._value);
}
}
_table.swap(newht._table);
}
}
//删除
bool Erase(const K& key)
{
//先找key是否存在,存在的话将其状态设置为DELETE
Node* node = Find(key);
if(node)
{
_table[node]._state = DELETE;
--_size;
return true;
}
return false;
}
size_t HashFunc(const K& key)
{
_HashFunc hf;
return hf(key) % _table.size();
}
protected:
vector<Node> _table;//哈希表的大小
size_t _size;//真正存入的数的大小
};
test.cpp
#include <iostream>
using namespace std;
#include "HashTable.h"
void TestHashTable()
{
/*int a[] = {89,18,49,58,9};
HashTable<int,int> ht;
for(size_t i = 0; i < sizeof(a)/sizeof(a[0]); ++i)
{
ht.Insert(a[i],i);
}*/
HashTable<string,string> s;
s.Insert ("sort","排序");
s.Insert ("Hash","哈希");
s.Insert ("string","字符串");
}
int main()
{
TestHashTable();
return 0;
}
二次探测:
其他代码相同,只需要改以下代码:
bool Insert(const K& key, const V& value)
{
CheckCapacity();
if (Find(key))
{
return false;
}
size_t i = 1;
size_t index = HashFunc(key);
size_t start = index;
while(_table[index]._state == EXIST)
{
index = start + i*i;
index %= _table.size();
++i;
if (index == _table.size())
{
index = 0;
}
}
_table[index]._key = key;
_table[index]._value = value;
_table[index]._state = EXIST;
_size++;
return true;
}
Node* Find(const K& key)
{
size_t index = HashFunc(key);
size_t start = index;
size_t i = 1;
while (_table[index]._state != EMPTY)
{
if (_table[index]._key == key)
{
if (_table[index]._state == EXIST)
{
return &_table[index];
}
else
{
return NULL;
}
}
//++index;
index = start + i*i;
index %= _table.size();
++i;
if (index == _table.size())
{
index = 0;
}
}
return NULL;
}
上一篇: 【Java作业】接口与多态
下一篇: 从RDD角度来剖析Spark内部原理