一、什么是key形式的哈希表
简单的说就是通过key的来查找key的值
二、线性探测
简单的来说就是遇到哈西冲突就查找下一个位置,看位置上是否有数据,假如有则继续查找,假如没有则将数据放入到当前位置
局限性:引发洪水式的冲突,冲突越来越多,找的越来越慢(因为找到的是一片冲突,找到空才停下来),效率低
二、定义状态数组和仿函数
1、定义状态数组
由于哈希表中每个位置的传值不知道设置为什么具体的值好并且删除后不知道设置成什么具体的值好(设置为-1万一保存的就是-1呢),所以设置一个状态数组用来,表示每一个为值上的值的状态,初始时设置为EMPTY,当前位置上存在数字设置为EXIST,要删除当前位置上的值设置为DELETE(也为懒删除法)enum Status
{
EXIST,
DELETE,
EMPTY,
};
2.定义仿函数
定义放函数的作用主要还是提高代码的复用性,对于不同类型的数据,其处理的函数是不同的,而其他地方的代码都是相似的,这样就可以定义一个仿函数专门来处理哈希函数不同的问题
//仿函数
template<class K>
struct DefaulstHashFuncer
{
size_t operator()(const K& key)
{
return key;
}
};
template<>
struct DefaulstHashFuncer <string>//特化string类型的仿函数
{
static size_t BKDRHash(const char * str)//<span style="color:#ff0000;">字符串的哈希算法</span>
{
unsigned int seed = 131; // 31 131 1313 13131 131313
unsigned int hash = 0;
while (*str)
{
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}
size_t operator()(const string& str)
{
return BKDRHash(str.c_str());
}
};
三、具体实现
1、函数的声明
template<class K, class HashFuncer = DefaulstHashFuncer<K>>
class HashTaable
{
public:
HashTaable();
HashTaable(size_t size);
HashTaable(const HashTaable<K, HashFuncer>& ht);
HashTaable<K, HashFuncer>& operator=(HashTaable<K, HashFuncer> ht);
~HashTaable();
public:
bool Insert(const K& key);
bool Find(const K& key);
bool Remove(const K& key);
void PrintTable();
void Swap(HashTaable<K>& ht);
protected:
size_t _HashFunc(const K& key);//哈希函数
int _Find(const K& key);
void _CheckCapacity();
protected:
K* _tables;
rsize_t _size;
Status* _status;//状态数组
size_t _capacity;
};
2、具体实现
(1)默认构造函数
HashTaable()
:_tables(NULL)
, _status(NULL)
, _size(0)
, _capacity(0)
{}
(2)构造函数,传入构造的哈希表的大小
HashTaable(size_t size)
:_tables(new K[size])
, _status(new Status[size])
, _size(0)
, _capacity(size)
{
//for (_status, EMPTY, sizeof(_status)*_size);//枚举类型不能用memsete因为memset是按字节处理
for (size_t i = 0; i < _capacity; ++i)//若用memset则里面为随机值
{
_status[i] = EMPTY;
}
}
(3)拷贝构造函数
HashTaable(const HashTaable<K, HashFuncer>& ht)
:_tables(NULL)
, _status(NULL)
, _size(0)
, _capacity(0)
{
HashTaable<K, HashFuncer> tmp(ht._capacity);
for (size_t i = 0; i <ht._capacity; ++i)
{
if (ht._status[i] == EXIST)//状态为删除时不用管
{
tmp.Insert(ht._tables[i]);
}
}
this->Swap(tmp);//冲突改变,相对位置改变
}
(4)operator=
HashTaable<K, HashFuncer>& operator=(HashTaable<K, HashFuncer> ht)
{
this->Swap(ht);
return *this;
}
(5)析构函数
~HashTaable()
{
if (_tables!=NULL)
{
delete[] _tables;
delete[] _status;
_tables = NULL;
_status = NULL;
}
}
(6)插入数据(如果数据本身在表中就存在,则返回false)
bool Insert(const K& key)
{
/*if (_size == _capacity)
{
cout << "Full" << endl;
return false;
}*/
_CheckCapacity();
size_t index = _HashFunc(key);
//线性探测
while (_status[index] == EXIST)
{
if (_tables[index] == key)
{
return false;
}
++index;
if (index == _capacity)
{
index = 0;
}
}
_status[index] = EXIST;
_tables[index] = key;
++_size;
return true;
}
(7)寻找数据
如果找到则返回true,没有找到返回false
bool Find(const K& key)
{
if (_Find(key) == -1)
{
return false;
}
return true;
}
(8)删除某个记录
使用懒删除法
bool Remove(const K& key)
{
int index = _Find(key);
if (index!=-1)
{
_status[index] = DELETE;
return true;
}
return false;
}
(9)打印整个哈希表
void PrintTable()
{
for (size_t i = 0; i < _capacity; ++i)
{
if (_status[i] == EXIST)
{
printf("[%d];E->", i);
cout << _tables[i] << endl;
}
else if (_status[i]==DELETE)
{
printf("[%d];D->", i);
cout <<_tables[i];
}
else
{
printf("[%d];N", i);
cout << endl;
}
}
}
(10)两个哈希表之间发生交换
void Swap(HashTaable<K>& ht)
{
std::swap(_tables, ht. _tables);
std::swap(_size, ht._size);
std::swap(_status, ht._status);
std::swap(_capacity, ht._capacity);
}
下面为保护的函数
(11)哈希函数
size_t _HashFunc(const K& key)//哈希函数
{
HashFuncer hf;
return hf(key) % _capacity;
}
(13)查找key值所在的位置
int _Find(const K& key)
{
size_t index = _HashFunc(key);//默认哈希表中一定是有空余的位置的
while (_status[index] != EMPTY)
{
if (_tables[index] == key&&_status[index] != DELETE)
{
return index;
}
++index;
if (index == _capacity)
{
index = 0;
}
}
return -1;
}
(14)增容
void _CheckCapacity()
{
if (_size * 10 >= _capacity * 7)
{
HashTaable<K> tmp(2 * _capacity);
for (size_t i = 0; i < _capacity; ++i)
{
if (_status[i] == EXIST)//状态为删除时不用管
{
tmp.Insert(_tables[i]);
}
}
this->Swap(tmp);//冲突改变,相对位置改变
}
}
三、测试用例
void Test()
{
HashTaable<string> ht1(10);//使用仿函数解决这个问题
ht1.Insert("小明");
ht1.Insert("杰克");
ht1.Insert("明小");
ht1.Insert("小军");
ht1.PrintTable();
cout << endl;
HashTaable<string> ht2(ht1);
ht2.Insert("123");
ht2.PrintTable();
cout << endl;
ht2 = ht1;
ht2.PrintTable();
}