函数声明 | 功能介绍 |
unordered_map
|
构造不同格式的 unordered_map 对象
|
函数声明 | 功能介绍 |
bool empty() const
|
检测 unordered_map 是否为空
|
size_t size() const
|
获取 unordered_map 的有效元素个数
|
函数声明 | 功能介绍 |
begin |
返回 unordered_map 第一个元素的迭代器
|
end |
返回 unordered_map 最后一个元素下一个位置的迭代器
|
cbegin |
返回 unordered_map 第一个元素的 const 迭代器
|
cend |
返回 unordered_map 最后一个元素下一个位置的 const 迭代器
|
函数声明 |
功能介绍
|
operator[] |
返回与 key 对应的 value ,没有一个默认值
|
函数声明 | 功能介绍 |
iterator fifind(const K& key)
|
返回 key在哈希桶中的位置
|
size_t count(const K& key)
|
返回哈希桶中关键码为 key 的键值对的个数
|
函数声明 | 功能介绍 |
insert |
向容器中插入键值对
|
erase |
删除容器中的键值对
|
clear() |
清空容器中有效元素个数
|
swap |
交换两个容器中的元素
|
函数声明 | 功能介绍 |
size_t bucket_count()const
|
返回哈希桶中桶的总个数
|
size_t bucket_size(size_t n)const
|
返回 n 号桶中有效元素的总个数
|
size_t bucket(const K& key)
|
返回元素 key 所在的桶号
|
参见 unordered_set在线文档说明
class Solution {
public:
int repeatedNTimes(vector& nums) {
int ret = 0;
unordered_map mp;
for(const auto & e: nums)
{
mp[e]++; //统计次数
}
for(const auto &e : mp)
{
if(e.second == nums.size() / 2)
{
ret = e.first;
break;
}
}
return ret;
}
};
class Solution {
public:
vector intersection(vector& nums1, vector& nums2) {
vector ret;
unordered_set s1,s2; //用于去重
for(const auto &e :nums1)
{
s1.insert(e);
}
for(const auto &e :nums2)
{
s2.insert(e);
}
unordered_map mp; //统计次数
for(const auto e: s1)
{
mp[e]++;
}
for(const auto e: s2)
{
mp[e]++;
if(mp[e] == 2)
ret.push_back(e);
}
return ret;
}
};
class Solution {
public:
vector uncommonFromSentences(string s1, string s2) {
vector ret;
s1 +=' '; //加空格好处理
s2 +=' '; //加空格好处理
unordered_map mp;
string tmp;
for(size_t i =0; i< s1.size();++i)
{
if(s1[i] != ' ')
{
tmp += s1[i];
}
else
{
mp[tmp]++;
tmp.clear();
}
}
for(size_t i =0; i< s2.size();++i)
{
if(s2[i] != ' ')
{
tmp += s2[i];
}
else
{
mp[tmp]++;
tmp.clear();
}
}
for(const auto &e : mp)
{
if(e.second == 1)
ret.push_back(e.first);
}
return ret;
}
};
对于两个数据元素的关键字 和 (i != j),有 != ,但有:Hash( ) == Hash( ),即:不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。 把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。 发生哈希冲突该如何处理呢?
引起哈希冲突的一个原因可能是:哈希函数设计不够合理。 哈希函数设计原则:
哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0 到m-1之间
哈希函数计算出来的地址能均匀分布在整个空间中
哈希函数应该比较简单
1. 线性探测
删除
采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他 元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标 记的伪删除法来删除一个元素。
// 哈希表每个空间给个标记
// EMPTY此位置空, EXIST此位置已经有元素, DELETE元素已经删除
enum State{EMPTY, EXIST, DELETE};
#pragma once
#include
#include
//根据STL源码库提供的扩容方案
static const unsigned long __stl_prime_list[28] =
{
53, 97, 193, 389, 769,
1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433,
1572869, 3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189, 805306457,
1610612741, 3221225473, 4294967291
};
static size_t GetNextPrime(size_t num)
{
for (size_t i = 0; i < 28; ++i)
{
if (__stl_prime_list[i] > num)
return __stl_prime_list[i];
}
return __stl_prime_list[27];
}
template
class Hash //如果是整型直接用自身做key
{
public:
size_t operator()(const K& key)
{
return key;
}
};
template<> //提供特化版本
class Hash
{
public:
size_t operator()(const std::string& key)
{
size_t value = 0;
for (const auto& e : key)
{
value *= 31; //通过数学推理得到的,可以31,131等。
value += e;
}
return value;
}
};
namespace CloseHash
{
enum Status //同来控制在位置的状态
{
EXIST,
EMPTY,
DELETE
};
template
class HashData //每个位置插入的数据
{
public:
std::pair _kv;
Status _status = EMPTY;
};
template>
class HashTable
{
public:
HashData* find(const K& key)
{
if (_tables.size() == 0)
return nullptr;
HashFunc hf;
size_t start = hf(key) % _tables.size();
size_t i = 0;
size_t index = start;
while (_tables[index]._status != EMPTY)
{
if (_tables[index]._kv.first == key && _tables[index]._status == EXIST)
{
return &_tables[index];
}
++i;
index = start + i;
index %= _tables.size();
}
return nullptr;
}
bool insert(const std::pair& kv)
{
HashData* ret = find(kv.first);
if (ret != nullptr)
return false;
if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7) //扩容,这里可以不同,java库中是0.75
{
//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
size_t newSize = _tables.size() == 0 ? 53 : GetNextPrime(_tables.size());
HashTable newHT;
newHT._tables.resize(newSize);
for (size_t i = 0; i < _tables.size(); ++i)
{
newHT.insert(_tables[i]._kv);
}
_tables.swap(newHT._tables);
}
HashFunc hf;
size_t start = hf(kv.first) % _tables.size();
size_t i = 0;
size_t index = start;
//线性探测 or 二次探测
while (_tables[index]._status == EXIST)
{
++i;
index = start + i;
//index = start + i * i;
index %= _tables.size();
}
_tables[index]._kv = kv;
_tables[index]._status = EXIST;
++_n;
return true;
}
bool erase(const K& key)
{
HashData* ret = find(key);
if (ret == nullptr)
return false;
ret->_status = DELETE;
--_n;
return true;
}
private:
std::vector> _tables;
size_t _n = 0; //有效数据个数
};
void CloseHashTest()
{
/*HashTable ht;
int array[] = { 2,12,22,32,42,52,62,72,82 };
for (const auto &e : array)
{
ht.insert(std::make_pair(e, e));
}
ht.erase(22);
ht.erase(32);
ht.erase(42);
ht.erase(52);*/
HashTable ht;
ht.insert(std::make_pair("sort", "排序"));
ht.insert(std::make_pair("map", "地图"));
ht.insert(std::make_pair("left", "左边"));
ht.insert(std::make_pair("right", "右边"));
ht.insert(std::make_pair("apples", "苹果"));
}
}
思考:哈希表什么情况下进行扩容?如何扩容?
载荷因子越大,空间占用率小,哈希冲突概率大。
载荷因子越小,空间占用率大,哈希冲突概率小。
因此:比散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷.
2.4.2 开散列
1. 开散列概念
开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码 归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。
2. 开散列实现
namespace OpenHash
{
template
class HashNode
{
public:
HashNode(const std::pair kv)
:_kv(kv)
,_next(nullptr)
{
}
std::pair _kv;
HashNode* _next;
};
template>
class HashTable
{
public:
typedef HashNode Node;
HashNode* find(const K& key)
{
HashFunc hf;
if (_tables.size() == 0)
return nullptr;
size_t index = hf(key) % _tables.size();
if (_tables[index] == nullptr)
return nullptr;
HashNode* cur = _tables[index];
while (cur != nullptr)
{
if (cur->_kv.first == key)
return cur;
cur = cur->_next;
}
return nullptr;
}
bool erase(const K& key)
{
if (_tables.empty())
return false;
HashFunc hf;
size_t index = hf(key) % _tables.size();
Node* prev = nullptr;
Node *cur = _tables[index];
while (cur != nullptr)
{
if (cur->_kv.first == key)
{
if (prev == nullptr)
{
_tables[index] = cur->_next;
}
else
{
prev->_next = cur->_next;
}
delete cur;
break;
}
prev = cur;
cur = cur->_next;
}
--_n;
return true;
}
bool insert(const std::pair& kv)
{
HashNode* ret = find(kv.first);
if (ret != nullptr)
return false;
HashFunc hf;
if ( _n == _tables.size()) //扩容
{
//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
size_t newSize = GetNextPrime(_tables.size());
std::vector NewVector;
NewVector.resize(newSize);
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
while (cur != nullptr)
{
Node* next = cur->_next;
size_t index = hf(cur->_kv.first) % NewVector.size();
cur->_next = NewVector[index];
NewVector[index] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(NewVector);
}
size_t index = hf(kv.first) % _tables.size();
Node* newnode = new Node(kv);
newnode->_next = _tables[index];
_tables[index] = newnode;
++_n;
return true;
}
private:
std::vector _tables;
size_t _n = 0; //有效数据个数
};
void OpenHashTest()
{/*
HashTable ht;
int array[] = { 2,12,22,32,42,52,62,72,82 };
for (const auto &e : array)
{
ht.insert(std::make_pair(e, e));
}
ht.insert(std::make_pair(92, 92));
ht.insert(std::make_pair(102, 102));
ht.erase(22);
ht.erase(32);
ht.erase(42);
ht.erase(52);*/
HashTable ht;
ht.insert(std::make_pair("sort", "排序"));
ht.insert(std::make_pair("map", "地图"));
ht.insert(std::make_pair("left", "左边"));
ht.insert(std::make_pair("right", "右边"));
ht.insert(std::make_pair("apples", "苹果"));
}
}
if ( _n == _tables.size()) //扩容
{
//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
size_t newSize = GetNextPrime(_tables.size());
std::vector NewVector;
NewVector.resize(newSize);
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
while (cur != nullptr)
{
Node* next = cur->_next;
size_t index = hf(cur->_kv.first) % NewVector.size();
cur->_next = NewVector[index];
NewVector[index] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(NewVector);
}
4. 开散列的思考
template
class Hash //如果是整型直接用自身做key
{
public:
size_t operator()(const K& key)
{
return key;
}
};
template<> //提供特化版本
class Hash
{
public:
size_t operator()(const std::string& key)
{
size_t value = 0;
for (const auto& e : key)
{
value *= 31; //通过数学推理得到的,可以31,131等。
value += e;
}
return value;
}
};
static const unsigned long __stl_prime_list[28] =
{
53, 97, 193, 389, 769,
1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433,
1572869, 3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189, 805306457,
1610612741, 3221225473, 4294967291
};
static size_t GetNextPrime(size_t num)
{
for (size_t i = 0; i < 28; ++i)
{
if (__stl_prime_list[i] > num)
return __stl_prime_list[i];
}
return __stl_prime_list[27];
}
#pragma once
#pragma once
#include
#include
template
class Hash
{
public:
size_t operator()(const K& key)
{
return key;
}
};
template<>
class Hash
{
public:
size_t operator()(const std::string& key)
{
size_t value = 0;
for (const auto& e : key)
{
value *= 31;
value += e;
}
return value;
}
};
static const unsigned long __stl_prime_list[28] =
{
53, 97, 193, 389, 769,
1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433,
1572869, 3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189, 805306457,
1610612741, 3221225473, 4294967291
};
static size_t GetNextPrime(size_t num)
{
for (size_t i = 0; i < 28; ++i)
{
if (__stl_prime_list[i] > num)
return __stl_prime_list[i];
}
return __stl_prime_list[27];
}
namespace OpenHash
{
template
class HashNode
{
public:
HashNode(const T & data)
:_data(data)
, _next(nullptr)
{
}
T _data;
HashNode* _next;
};
template //需要提前声明
class HashTable;
template
class HashTableIterator
{
public:
typedef HashNode Node;
typedef HashTableIterator Self;
HashTableIterator(Node* node, HashTable* pht)
:_node(node)
,_pht(pht)
{
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
if (_node->_next != nullptr) //说明当前桶还没走完
{
_node = _node->_next;
}
else
{
HashFunc hf;
KeyOfT kot;
size_t index = hf(kot(_node->_data)) % _pht->_tables.size(); //这里指针访问不了私有成员,可以用友元
++index;
while (index < _pht->_tables.size())
{
if (_pht->_tables[index] != nullptr)
{
break;
}
else
{
++index;
}
}
//走到有两中情况
if (index == _pht->_tables.size())
{
_node = nullptr;
}
else
{
_node = _pht->_tables[index];
}
}
return *this;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
private:
Node* _node;
HashTable* _pht;
};
template>
class HashTable
{
public:
typedef HashNode Node;
typedef HashTableIterator iterator;
template
friend class HashTableIterator; //声明友元
/*HashTable() //需要在初始化列表初始化vector和_n;所以需要提供默认构造
{
}*/
HashTable() = default; // c++11
HashTable(const HashTable& s)
{
_tables.resize(s._tables.size());
for (size_t i = 0; i < s._tables.size(); ++i)
{
Node* cur = s._tables[i];
while (cur != nullptr)
{
Node* copy = new Node(cur->_data);
copy->_next = _tables[i];
_tables[i] = copy;
cur = cur->_next;
}
}
}
HashTable& operator=(HashTable s)
{
swap(_n, s._n);
_tables.swap(s._tables);
return *this;
}
iterator begin()
{
for (size_t i = 0; i < _tables.size(); ++i)
{
if (_tables[i] != nullptr)
return iterator(_tables[i], this); //把表指针传递过去
}
return end();
}
iterator end()
{
return iterator(nullptr, this);
}
iterator find(const K& key)
{
HashFunc hf;
KeyOfT kot;
if (_tables.size() == 0)
return end();
size_t index = hf(key) % _tables.size();
if (_tables[index] == nullptr)
return end();
HashNode* cur = _tables[index];
while (cur != nullptr)
{
if (kot(cur->_data) == key)
return iterator(cur,this);
cur = cur->_next;
}
return end();
}
bool erase(const K& key)
{
if (_tables.empty())
return false;
HashFunc hf;
size_t index = hf(key) % _tables.size();
Node* prev = nullptr;
Node* cur = _tables[index];
while (cur != nullptr)
{
if (kot(cur->_data) == key)
{
if (prev == nullptr)
{
_tables[index] = cur->_next;
}
else
{
prev->_next = cur->_next;
}
delete cur;
break;
}
prev = cur;
cur = cur->_next;
}
--_n;
return true;
}
std::pair insert(const T& data)
{
KeyOfT kot;
//HashNode* ret = find(kot(data));
iterator ret = find(kot(data));
if (ret != end())
return std::make_pair(ret,false);
HashFunc hf;
if (_n == _tables.size()) //扩容
{
//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
size_t Newsize = GetNextPrime(_tables.size()); //根据STL源码库扩容都是素数
std::vector NewVector;
NewVector.resize(newSize);
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
while (cur != nullptr)
{
Node* next = cur->_next;
size_t index = hf(kot(cur->_data)) % NewVector.size();
cur->_next = NewVector[index];
NewVector[index] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(NewVector);
}
size_t index = hf(kot(data)) % _tables.size();
Node* newnode = new Node(data);
newnode->_next = _tables[index];
_tables[index] = newnode;
++_n;
return std::make_pair(iterator(newnode, this), true);
}
private:
std::vector _tables;
size_t _n = 0; //有效数据个数
};
}
#pragma once
#pragma once
#include"Hash.hpp"
namespace OpenHash
{
template> //这里第四个模板参数我就不想写了
class unodered_map
{
public:
class MapOfT
{
public:
const K& operator()(const std::pair & kv)
{
return kv.first;
}
};
typedef typename HashTable, MapOfT, hash>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
std::pair insert(const std::pair& kv)
{
return _ht.insert(kv);
}
iterator find(const K& key)
{
return _ht.find(key);
}
bool erase(const K& key)
{
return _ht.erase(key);
}
private:
HashTable, MapOfT, hash> _ht;
};
void unordered_map_test()
{
/*unodered_map um;
um.insert(std::make_pair(1, 1));
um.insert(std::make_pair(11, 11));
um.insert(std::make_pair(21, 21));
um.insert(std::make_pair(31, 31));
um.insert(std::make_pair(41, 41));
um.insert(std::make_pair(51, 51));*/
unodered_map um;
um.insert(std::make_pair("sort", "排序"));
um.insert(std::make_pair("map", "地图"));
um.insert(std::make_pair("left", "左边"));
um.insert(std::make_pair("right", "右边"));
auto it = um.begin();
while (it != um.end())
{
std::cout << it->first << ":" << it->second << std::endl;
++it;
}
std::cout << std::endl;
unodered_map < std::string, std::string > um2 = um;
auto rit = um2.begin();
while (rit != um2.end())
{
std::cout << rit->first << ":" << rit->second << std::endl;
++rit;
}
}
}
3.3 unordered_set
#pragma once
#include"Hash.hpp"
namespace OpenHash
{
template>
class unordered_set
{
public:
class SetOfT
{
public:
const K& operator()(const K& key)
{
return key;
}
};
typedef typename HashTable::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
std::pair insert(const K& key)
{
return _ht.insert(key);
}
bool earse(const K& key)
{
return _ht.erase();
}
iterator find(const K& key)
{
return _ht.find(key);
}
private:
HashTable _ht;
};
void test_unordered_set()
{
//unordered_set us;
/*us.insert(4);
us.insert(14);
us.insert(34);
us.insert(7);
us.insert(24);
us.insert(17);*/
unordered_set < std::string> us;
us.insert("sort");
us.insert("map");
us.insert("left");
us.insert("right");
us.insert("apples");
}
}