哈希表增加元素
增加元素注意两点:
- 往后遍历寻找空闲位置是环形遍历,否则访问会数组越界
- 发生哈希冲突找空闲位置的时候,有两种情况:这个位置一直是空的没放过元素;这个位置是空的,以前放过元素后来被删除了。
哈希表查询:
在线性探测写代码要注意:
当我们在去找value的时候,先计算哈希值发现它应该放在下标3的位置,取出这个位置的元素却不等于value,说明但是放value的时候发生哈希冲突了,访问4的时候发现位置是空的,
- 如果这个位置一直是空的,没放过元素。此时不需要再往后找value了。因为如果这个位置一直是空的,那当时value要放的话肯定就放在这里了。
- 如果这个位置是空的,以前放过元素,但后来被删除了。此时需要继续往后搜索。因为但是放value的时候,有可能这个位置元素还在,所以value往后找了个位置放了。
哈希表删除操作
我们在删除桶里面的元素并没有真的删除,只是把桶的状态改变了。
哈希表线性探测法代码实现
#include<iostream>
using namespace std;
enum State
{
STATE_UNUSE,//从未使用过的桶
STATE_USING,//正在使用的桶
STATE_DEL,//元素被删除的桶
};
class Bucket
{
public:
Bucket(int key=0,State state=STATE_UNUSE)
:_key(key),_state(state){
}
int _key;//存储数据
State _state;//存储状态
};
class HashTable
{
private:
Bucket* _table;//动态开辟的哈希表
int _tableSize;//桶的大小
int _useBucketNum;//正在使用的桶个数
double _loadFactor;//装载因子
//在C++11以后,静态常量的整型类型在类里面可以直接初始化
static const int _primeSize = 10;//素数大小
static int _primes[_primeSize];//素数表
int _primeIdx;//当前使用的素数下标
void expand()
{
++_primeIdx;
if (_primeIdx == _primeSize)
{
throw "hashtable is too large,can not expand anymore";
}
Bucket* newTable = new Bucket[_primes[_primeIdx]];
for (int i = 0; i < _tableSize; i++)
{
if (_table[i]._state == STATE_USING)
{
int idx = _table[i]._key % _primes[_primeIdx];
int k = idx;
do
{
if (newTable[k]._state == STATE_UNUSE)
{
newTable[k]._key = _table[i]._key;
newTable[k]._state = STATE_USING;
break;
}
k = (k + 1) % _primes[_primeIdx];
} while (k != idx);
}
}
delete[]_table;
_table = newTable;
_tableSize = _primes[_primeIdx];
}
public:
HashTable(int size = _primes[0], double loadFactor = 0.75)
:_useBucketNum(0), _loadFactor(loadFactor), _primeIdx(0)
{
//把用户传入的size调整到最近的比较大的素数上
if (size != _primes[0])
{
for (; _primeIdx < _primeSize; _primeIdx++)
{
if (_primes[_primeIdx] > size)
{
break;
}
}
//用户传入的size过大已经超过了最后一个数,调整为最后一个数
if(_primeIdx == _primeSize)
_primeIdx--;
}
_tableSize = _primes[_primeIdx];
_table = new Bucket[_tableSize];
}
~HashTable()
{
delete[]_table;
_table = nullptr;
}
bool insert(int key)
{
//考虑扩容
double factor = _useBucketNum * 1.0 / _tableSize;
if (factor > 0.75)
{
expand();
}
int idx = key % _tableSize;
int i = idx;
do
{
if (_table[i]._state == STATE_UNUSE)
{
_table[i]._key = key;
_table[i]._state = STATE_USING;
_useBucketNum++;
return true;
}
i = (i + 1) % _tableSize;
} while (i != idx);
}
bool erase(int key)
{
int idx = key % _tableSize;
int i = idx;
do
{
if (_table[i]._state == STATE_USING && _table[i]._key == key)
{
_table[i]._state = STATE_DEL;
_useBucketNum--;
break;
}
i = (i + 1) % _tableSize;
} while (_table[i]._state != STATE_UNUSE && i != idx);
return true;
}
bool find(int key)
{
int idx = key % _tableSize;
int i = idx;
do
{
if (_table[i]._key == key && _table[i]._state == STATE_USING)
{
return true;
}
i = (i + 1) % _tableSize;
} while (_table[i]._state != STATE_UNUSE && i != idx);
return false;
}
};
int HashTable::_primes[_primeSize] = {
3,7,23,47,97,251,443,911,1471,42773 };
int main()
{
HashTable htable;
htable.insert(12);
htable.insert(24);
htable.insert(38);
htable.insert(15);
htable.insert(14);
cout << htable.find(12) << endl;
htable.erase(12);
cout << htable.find(12) << endl;
}