我还是曾经的那个少年
1.概念
通过其要存储的值与存储的位置建立映射关系。
如:基数排序也是运用了哈希开放定址法的的思想。
弊端:仅适用于数据集中的情况
2.开放定址法
问题:按照上述哈希的方式,向集合插入数据为44,会出现什么问题呢?
哈希冲突。
即:不同关键字通过相同的哈希方式计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
3.哈希冲突的解决方式
闭散列和开散列。(这次主要讲闭散列)
3.1闭散列
闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。那如何寻找下一个空位置呢?
1.线性探测
比如上面的场景中,现在需要插入元素44,先通过哈希函数计算哈希地址,hashAddr为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。
- 插入
- 通过哈希函数获取待插入元素在哈希表中的位置
- 如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突, 使用线性探测找到下一个空位置,插入新元素。
- 删除
采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影
响其他元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。
因此线性探测采用标记的伪删除法来删除一个元素。
通过添加枚举常量,来标记相应位置上是否存在或为空等状态。
enum STASTE
{
EXIST,
EMPTY,
DELETE
};
template<class K, class V>
struct HashData
{
pair<K, V> _kv;
STASTE _state = EMPTY;
};
线性探测:找到空(状态为:EMPTY),没找到才返回
3.1.1扩容问题
4.插入的key值为字符串
我们需要将该字符串转换成相应的整数再进行映射。字符哈希算法。
5.模拟实现
//整型(同时还解决了负数和浮点数的问题)
template<class K>
struct DefaultHashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
//模板特化
template<>
struct DefaultHashFunc<string>
{
size_t operator()(const string& str)
{
// BKDR
size_t hash = 0;
for (auto ch : str)
{
hash *= 131;
hash += ch;
}
return hash;
}
};
namespace open_address
{
enum STASTE
{
EXIST,
EMPTY,
DELETE
};
template<class K, class V>
struct HashData
{
pair<K, V> _kv;
STASTE _state = EMPTY;
};
template<class K, class V, class HashFunc = DefaultHashFunc<K>>
class HashTable
{
public:
HashTable()
{
_table.resize(10);
}
bool insert(const pair<K, V>& kv)
{
//是否存在
if (find(kv.first))
return false;
//扩容
if (_n * 10 / _table.capacity() >= 7)
{
//类似现代写法
HashTable<K, V, HashFunc> tmp;
tmp.getTable().resize(_table.capacity() * 2);
//遍历旧数据,重新映射到新空间
for (size_t i = 0; i < _table.size(); i++)
{
if (_table[i]._state == EXIST)
tmp.insert(_table[i]._kv);
}
_table.swap(tmp.getTable());
}
//线性探索
HashFunc hf;
size_t hashi = hf(kv.first) % _table.size();
while (_table[hashi]._state == EXIST)
{
++hashi;
hashi %= _table.size();
}
_table[hashi]._kv = kv;
_table[hashi]._state = EXIST;
++_n;
return true;
}
HashData<const K, V>* find(const K& key)
{
HashFunc hf;
//先找到相应的映射位置,是则返回,否则继续往后查找到空为止
size_t hashi = hf(key) % _table.size();
while (_table[hashi]._state != EMPTY)
{
if (hf(_table[hashi]._kv.first) == hf(key))
return (HashData<const K, V>*) & _table[hashi];
++hashi;
hashi %= _table.size();
}
return nullptr;
}
bool erase(const K& key)
{
HashData<const K, V>* ret = find(key);
if (ret == nullptr || ret->_state == DELETE)
return false;
ret->_state = DELETE;
--_n;
return true;
}
vector<HashData<K, V>>& getTable()
{
return _table;
}
private:
vector<HashData<K, V>> _table;
size_t _n = 0; //记录存储有效数据个数
};
}
感觉不错的可以点赞+收藏咯!!
谢谢大家