模拟实现【哈希】

哈希

unordered系列关联式容器

unordered_set

和set接口和功能几乎一样。

unordered_map

1.unordered_map是储存<key,value>键值对的关联式容器,允许通过key快速索引与其对应的value。 2.在unordered_map中,键值用于唯一的标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。 3.在内部,unordered_map没有对<key,value>按照特定的舒徐排序,为了在常熟范围内找到key所对应的value,unordered_map将相同的键值放在了相同的桶中。 4.unordered_map容器通过key访问单个元素比map块,但它在遍历元素自己的范围迭代方面效率低。 5.unordered_map实现了直接访问操作符(operator[])。它允许key作为参数直接访问value。 6.它的迭代器至少是向前迭代器。

unordered_map接口

1.unordered_map构造

函数声明功能介绍
unordered_map构造不同格式的unordered_map对象

2.unordered_map容量

函数声明功能
bool empty()const检测为空
size_t size()const获取有效元素的个数

3.unordered_map迭代器

函数声明功能
begin返回第一个元素的迭代器
end返回最后一个元素的下一个位置的迭代器
cbeginconst begin()const
cendconst end()const

4.unordered_map的元素访问

函数声明功能
operator[]返回key对应的value,没有固定的默认值(取决于key)
*注意:该函数实际上调用哈希桶的插入操作,参数key与v()构造一个默认值往底层哈希桶插入
插入成功,返回v() 失败返回key对应的value的值*

5.unordered_map查询

函数声明功能
iterator find(const K& key)返回key在哈希桶中的位置
size_t count(const K& key)返回哈希桶中有关key的键值对的个数
unordered_map count返回的最大值为1,原因:不能重复

6.unordered_map的修改

函数声明功能
insert插入键值对
erase删除键值对
void clear()清空有效元素的个数
void swap(unordered_map&)交换两个容器中的元素(指针交换)

7.unordered_map操作

函数声明功能
size_t bucket count()const返回哈希桶中的总个数
size_t bucket size(size_t n)const返回n号桶有效元素的总个数
size_tbucket(const K& key)返回元素key所在的桶号

比较map和unordered_map的效率

#include<iostream>
#include<vector>
#include<algorithm>
#include<unordered_map>
#include<map>
#include<time.h>
using namespace std;
​
void test_map()
{
    const int n = 100000;
    srand(time(0));
    vector<int> v;
    v.reserve(n);
    for (int i = 0; i < n; i++)
    {
        v.push_back(rand()+i);//伪随机只有3.5w左右
    }
    //测试插入的速度
    map<int, int> p;
    unordered_map<int, int> up;
    clock_t start1 = clock();
    for (auto e : v)
    {
        p.insert(make_pair(e, e));
    }
    clock_t end1 = clock();
​
    
    clock_t start2 = clock();
    for (auto e : v)
    {
        up.insert(make_pair(e, e));
    }
    clock_t end2 = clock();
​
    //查找速度
    clock_t start3 = clock();
    for (auto e : v)
    {
        p.find(e);
    }
    clock_t end3 = clock();
​
​
    clock_t start4 = clock();
    for (auto e : v)
    {
        up.find(e);
    }
    clock_t end4 = clock();
​
    //删除速度
    clock_t start5 = clock();
    for (auto e : v)
    {
        p.erase(e);
    }
    clock_t end5 = clock();
​
​
    clock_t start6 = clock();
    for (auto e : v)
    {
        up.erase(e);
    }
    clock_t end6 = clock();
​
    cout << "插入速度:" << endl << "map:" << end1 - start1 << endl << "unordered_map:" << end2 - start2 << endl;
    cout << "find速度:" << endl << "map:" << end3 - start3 << endl << "unordered_map:" << end4 - start4 << endl;
    cout << "erase速度:" << endl << "map:" << end5 - start5 << endl << "unordered_map:" << end6 - start6 << endl;
​
}
int main()
{
    test_map();
    return 0;
}
​

底层结构

unordered系列之所以效率比较高,是因为底层使用了哈希结构。

哈希概念

顺序结构及平衡树中,元素关键码与其存储位置间没有对应关系,因此,在查找一个元素时,必须多次经过关键码的比较。顺序查找的时间复杂度为O(N).平衡树中为树的高度,即O(logN),搜索的效率取决于搜索过程中元素的比较次数。

理想的搜索方法:可以不经过任何比较,一次直接从表中得到的要搜索的元素。 如果构造一种存储结构,通过某种函数(hashfunc)使元素的存储位置与他的该案件吗之间建立一一映射关系,那么在查找时通过该函数可以很快找到该元素。

该结构中:

插入元素 根据待插入元素的关键码,以此函数计算该元素的储存位置并按此位置进行存放。

存放元素 对元素的关键码进行同样的计算,把求得的函数值当作元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

该方式为哈希(散列)方法,哈希方法中使用的函数转化称为哈希(散列)函数,构造出来的结构称为哈希表(散列表)

int a[] = {1,7,4,6,5,9};
hashi(key)= key % capicity;

用这种方法,几乎不用做多次关键码比较,搜索速度特别快。 但是我要insert(17)?,会出现哈希冲突的问题。

哈希冲突

概念

不同关键字通过相同的哈希数计算相同的哈希地址,这种现象称为哈希冲突。 把具有相同哈希地址的数据元素称为”同义词“

哈希函数

能够触发哈希冲突,可能是哈希函数设计不够合理。 设计原则 1.哈希函数的定义域必须包括需要储存的全部关键码,而如果散列表允许有m个地址,其值域必须到[0,m-1]之间 2.哈希函数算出来的地址能均匀的分布在整个空间中。 3.哈希函数应该比较简单。

常见哈希函数 1.直接定址法 取关键字的某个线性函数为散列地址:Hash(key) = A*key+B 优点:简单,均匀 缺点:需要事先知道关键字的分布情况 使用场景:适合查找比较小且连续的情况

2.除留余数法 设散列表中允许的地址数为m,取一个不大于m,但接近或者等于m的质数作为除数,按照哈希函数:Hash(key) = key % p( p <= m ),将关键码换成哈希函数。

注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突。

哈希冲突的解决

闭散列开散列

闭散列

闭散列:开放地址法,发生哈希冲突的时候,如果哈希表未装满,说明在哈希表中还有空位置,那么可以把key存放到冲突位置的”下一个“空位置中去。

线性探测 从发生冲突的位置开始,一次向后探测,直到寻找到下一个空位置为止。

思考: 哈希表啥时候可以进行扩容? 荷载因子: a = 填入表中的元素/散列表的长度 a在[0.7,0.8]为佳。

线性探测特性
优点实现非常简单
缺点一旦发生哈希冲突,却数据量较大,容易引发”堵塞“,导致搜索效率降低

二次探测 为避免数据过于集中导致的”堵塞“,使用二次探测可以避免该问题。 在线性探测中i = 1,在二次探测中i的值可以随机

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次,因此只要表中有一半的空位置,既不会存在表装满的问题。在搜索时可以不考虑表装满的情况,但插入时必须保证a<=0.5

开散列的最大缺陷就是空间利用率比较低。这也是哈希缺陷 相关代码: 初版,能用但不是目前最优秀的表

#include<iostream>
#include<vector>
#include<string>
using namespace std;
​
enum State
{
    EMPTY,
    EXSIST,
    DELETE
};
template< class K,class V>
struct HashData
{
    pair<K, V> _kv;
    State _state = EMPTY;//获取某个桶的状态,给个缺省值。
    
};
//仿函数,完成对内置类型和string的支持
template<class K>
struct DefaultHash
{
    size_t operator()(const K& key)
    {
        return (size_t)key;
    }
};
​
//模板特化,支持string
template<>
struct DefaultHash<string>
{
    //使用BKDR算法
    size_t operator()(const string& key)
    {
        size_t hash = 0;
        for (auto ch : key)
        {
            hash = hash * 131 + ch;
        }
        return hash;
    }
};
​
template<class K,class V,class Hashfunc = DefaultHash<K>>
class HashTable
{
    typedef HashData<K, V> Data;
public:
    bool insert(const pair<K, V>& kv)
    {
        //hash的扩容必须处理,一般情况下,hash是绝对不会被存满的。
        //就像二叉搜索树一样,欸一个关键字因子
        //
        Hashfunc hf;
        //去重
        if (find(kv.first))//存在就不要再插入了。
            return false;
        //首先判读size()是不是为0?
        //判断关键字因子是不是超过了70%
        if (_table.size() == 0 || _n * 10 / _table.size() >= 7)//这里次序不能颠倒,否则会报错
        {
            size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
            //一旦更新,个元素相对于hashtable的位置就会出错,需要单独调整
            //现代写法:通过船舰一个新的hashtable,通过调用insert函数,此时不会走if语句
            //编译器跑完以后,交换指针即可。
            HashTable<K, V> hashtable;
            hashtable._table.resize(newsize);
            //
            for (auto& e : _table)
            {
                if(e._state == EXSIST)
                    hashtable.insert(e._kv);
            }
            hashtable._table.swap(_table);//交换指针,因为hashtable是临时对象,生命周期结束后,会自己析构。
        }
​
        size_t starti = hf(kv.first);//获取key
        //将key%=size()获取映射在hashtable中的位置
        starti %= _table.size();
        size_t hashi = starti;
​
        //二次查找、扫描
        int i = 1;//采取的是闭散列法,挪到下一个位置上
        while (_table[hashi]._state == EXSIST)//当桶的状态为EXIST时,不能存放数据没。
        {
            hashi = hashi + i;
        }
​
        //赋值
        _table[hashi]._kv = kv;
        //更改状态
        _table[hashi]._state = EXSIST;
        _n++;
        return true;
    }
    Data* find(const K& key)//找到返回指针,找不到返回nullptr
    {
        if (_table.size() == 0)
        {
            return nullptr;
        }
        //为空的时候就停止,empty和delete继续
        //闭散列的存储方法,所找的数据都应该在[hashi,_table.end()]内,为空就结束了
        Hashfunc hf;
        size_t starti = hf(key);
        starti %= _table.size();
        size_t hashi = starti;
​
        int i = 1;
        while (_table[hashi]._state != EMPTY)
        {
            if (_table[hashi]._state == EXSIST && hf(_table[hashi]._kv.first) == hf(key))
                //删除是伪删除法,本质是数据的覆盖,所以要state 和 值双重排定
            {
                return &_table[hashi];
            }
            hashi = hashi + i;
            hashi %= _table.size();//防止hashi越界。
        }
        return nullptr;
    }
​
    bool erase(const K& key)
    {
        //为空,就不要删除了
        if (_table.size() == 0)
            return false;
        //找到元素
        Data* ret = find(key);
​
        if (ret)//存在
        {
            ret->_state = DELETE;
            _n--;//关键字因子个数要处理,因为exist变化了。
            return true;
        }
        else
        {
            return false;
        }
    }
private:
    vector<Data> _table;
    size_t _n = 0; //关键字因子的个数
};

开散列

概念

开散列法又叫做链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址空间的关键码归于同一自己和,每个子集合成为一个桶,各个桶中的元素通过一个单链表连起来,各链表的头节点储存在哈希表中。

开散列增容:

桶数量是一定数量的,随着新元素的插入,每个桶中的元素不断增多,极端情况下,可能导致一个同种链表节点非常多,会影响哈希表的性能,所以条件合理的情况下,要对哈希表进行扩容。

扩容条件:每个哈希桶中刚好挂一个节点,继续插入元素时,每一次都会发生哈希冲突。因此,在元素个数刚好等于桶的个数时,可以进行扩容。

相关代码:

#include<iostream>
#include<vector>
#include<string>
using namespace std;
​
enum State
{
    EMPTY,
    EXSIST,
    DELETE
};
template< class K,class V>
struct HashData
{
    pair<K, V> _kv;
    State _state = EMPTY;//获取某个桶的状态,给个缺省值。
    
};
//仿函数,完成对内置类型和string的支持
template<class K>
struct DefaultHash
{
    size_t operator()(const K& key)
    {
        return (size_t)key;
    }
};
​
//模板特化,支持string
template<>
struct DefaultHash<string>
{
    //使用BKDR算法
    size_t operator()(const string& key)
    {
        size_t hash = 0;
        for (auto ch : key)
        {
            hash = hash * 131 + ch;
        }
        return hash;
    }
};
​
template<class K,class V,class Hashfunc = DefaultHash<K>>
class HashTable
{
    typedef HashData<K, V> Data;
public:
    bool insert(const pair<K, V>& kv)
    {
        //hash的扩容必须处理,一般情况下,hash是绝对不会被存满的。
        //就像二叉搜索树一样,欸一个关键字因子
        //
        Hashfunc hf;
        //去重
        if (find(kv.first))//存在就不要再插入了。
            return false;
        //首先判读size()是不是为0?
        //判断关键字因子是不是超过了70%
        if (_table.size() == 0 || _n * 10 / _table.size() >= 7)//这里次序不能颠倒,否则会报错
        {
            size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
            //一旦更新,个元素相对于hashtable的位置就会出错,需要单独调整
            //现代写法:通过船舰一个新的hashtable,通过调用insert函数,此时不会走if语句
            //编译器跑完以后,交换指针即可。
            HashTable<K, V> hashtable;
            hashtable._table.resize(newsize);
            //
            for (auto& e : _table)
            {
                if(e._state == EXSIST)
                    hashtable.insert(e._kv);
            }
            hashtable._table.swap(_table);//交换指针,因为hashtable是临时对象,生命周期结束后,会自己析构。
        }
​
        size_t starti = hf(kv.first);//获取key
        //将key%=size()获取映射在hashtable中的位置
        starti %= _table.size();
        size_t hashi = starti;
​
        //二次查找、扫描
        int i = 1;//采取的是闭散列法,挪到下一个位置上
        while (_table[hashi]._state == EXSIST)//当桶的状态为EXIST时,不能存放数据没。
        {
            hashi = hashi + i;
        }
​
        //赋值
        _table[hashi]._kv = kv;
        //更改状态
        _table[hashi]._state = EXSIST;
        _n++;
        return true;
    }
    Data* find(const K& key)//找到返回指针,找不到返回nullptr
    {
        if (_table.size() == 0)
        {
            return nullptr;
        }
        //为空的时候就停止,empty和delete继续
        //闭散列的存储方法,所找的数据都应该在[hashi,_table.end()]内,为空就结束了
        Hashfunc hf;
        size_t starti = hf(key);
        starti %= _table.size();
        size_t hashi = starti;
​
        int i = 1;
        while (_table[hashi]._state != EMPTY)
        {
            if (_table[hashi]._state == EXSIST && hf(_table[hashi]._kv.first) == hf(key))
                //删除是伪删除法,本质是数据的覆盖,所以要state 和 值双重排定
            {
                return &_table[hashi];
            }
            hashi = hashi + i;
            hashi %= _table.size();//防止hashi越界。
        }
        return nullptr;
    }
​
    bool erase(const K& key)
    {
        //为空,就不要删除了
        if (_table.size() == 0)
            return false;
        //找到元素
        Data* ret = find(key);
​
        if (ret)//存在
        {
            ret->_state = DELETE;
            _n--;//关键字因子个数要处理,因为exist变化了。
            return true;
        }
        else
        {
            return false;
        }
    }
private:
    vector<Data> _table;
    size_t _n = 0; //关键字因子的个数
};
​
namespace Bucket
{
    template<class K,class V>
    struct HashNode
    {
        pair<K, V> _kv;
        HashNode<K, V>* _next;
        //构造函数
        HashNode(const pair<K, V>& kv)
            :_kv(kv)
            ,_next(nullptr)
        {}
    };
    template<class K>
    struct DefaultHash
    {
        size_t operator()(const K& key)
        {
            return (size_t)key;
        }
    };
​
    template<>
    struct DefaultHash<string>
    {
        size_t operator()(const string& key)
        {
            size_t hash = 0;
            for (auto ch : key)
            {
                hash += ch * 131;
            }
            return hash;
        }
    };
​
​
    template<class K,class V>
    class HashTable
    {
        typedef HashNode<K, V> Node;
    public:
        ~HashTable()
        {
            for (int i = 0; i < _table.size(); i++)
            {
                Node* cur = _table[i];
                while (cur)
                {
                    Node* next = cur->_next;
                    delete cur;
                    cur = next;
                }
            }
        }
        bool insert(const pair<K, V>& kv)
        {
            if (find(kv.first))
                return false;
            //扩容否?
            if (_n == _table.size())
            {
                size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;
                //重新映射
                HashTable<K, V> newHT;
                newHT.resize(newSize,nullptr);
                for (int i = 0; i < _table.size(); i++)
                {
                    Node* cur = _table[i];
                    while (cur)
                    {
                        newHT.insert(cur->_kv);
                        cur = cur->_next;
                    }
                }
​
                newHT._table.swap(_table);//需要析构函数
                //原因:vector自己调用析构函数,但是Node节点却不会自己析构,因为其是内置类型的。
            }
            size_t starti = kv.first;
            starti %= _table.size();
            size_t hashi = starti;
​
            Node* newnode = new Node(kv);
            //头插法链接
            newnode->_next = _table[hashi];
            _table[hashi] = newnode;
            _n++;
        }
​
        Node* find(const K& key)
        {
            if (_table.size() == 0)
            {
                return nullptr;
            }
            size_t starti = key;
            size_t hashi = starti %= _table.size();
​
            Node* cur = _table[hashi];//找到key所在的哪个桶
​
            while (cur)
            {
                if (cur->_kv.first == key)
                {
                    return true;
                }
                cur = cur->_next;
            }
​
            return false;
        }
        bool erase(const K& key)
        {
            if (_table.size() == 0)
                return false;
​
            size_t starti = key;
            size_t hashi = starti %= _table.size();
​
            Node* cur = _table[hashi];
            Node* prev = nullptr;
            while (cur)
            {
                if (prev == nullptr)//头节点
                {
                    _table[hashi] = cur->_next;
                    delete cur;
                    return true;
                }
                else//非头结点
                {
                    prev->_next = cur->_next;
                    delete cur;
                    return true;
                }
                //更新
                prev = cur;
                cur = cur->_next;
            }
​
            return false;
        }
​
    private:
        vector<Node*> _table;
        size_t _n = 0;
    };
}

哈希的模拟实现

类似于map和set的封装 template<class K,class T,class KeyOfT(获取key),class hashFunc(获取hashi,映射)>

hash.h

#pragma once
#include<iostream>
#include<vector>
#include<string>
using namespace std;


template<class K>
struct DefaultHash
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

template<>
struct DefaultHash<string>
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto ch : key)
		{
			hash = hash*131 + ch;
		}
		return hash;
	}
};

namespace Bucket
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;
		//构造函数
		HashNode(const T& data)
			:_data(data)
			, _next(nullptr)
		{}
	};

	//set
	//template<class K,class K,class KeyOfT,class hashfunc>
	/*map
	template<class K,class V,class KeyOfT,class hashFunc>*/
	//必须前置声明HashTable
	template<class K, class T, class KeyOfT, class hashFunc>
	class HashTable;

	template<class K, class T, class KeyOfT, class hashFunc>
	class __HTIterator
	{
		typedef HashNode<T> Node;
		typedef __HTIterator<K, T, KeyOfT, hashFunc> Self;
	public:
		Node* _node;//迭代器返回的是节点
		HashTable<K, T, KeyOfT, hashFunc>* _pht;//表的指针

		//进行迭代器的处理
		__HTIterator(Node* node, HashTable<K, T, KeyOfT, hashFunc>* pht)
			:_node(node)
			, _pht(pht)
		{}

		//前置++
		Self& operator++()
		{
			if (_node->_next)//下一个不为空
			{
				_node = _node->_next;
			}
			else//为空
			{
				//确定_node的位置
				KeyOfT kot;
				hashFunc hf;

				size_t hashi = hf(kot(_node->_data));//转成key->hashi
				hashi %= _pht->_table.size();//找到映射位置

				++hashi;
				for (; hashi < _pht->_table.size(); hashi++)
				{
					if (_pht->_table[hashi])//找到
					{
						_node = _pht->_table[hashi];
						break;
					}
				}
				if (hashi == _pht->_table.size())//没找到
				{
					_node = nullptr;
				}
			}
			return *this;
		}
		Self operator++(int)
		{
			Self tmp(*this);

			++(this);
			return tmp;
		}

		T& operator*()
		{
			return _node->_data;
		}
		T* operator->()
		{
			return &_node->_data;
		}
		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
		bool operator!=(const Self& s)const
		{
			return _node != s._node;
		}

	};

	template<class K, class T, class KeyOfT, class hashFunc>
	class HashTable
	{
		template<class K, class T, class KeyOfT, class hashFunc>
		friend class __HTIterator;

		typedef HashNode<T> Node;
	public:
		typedef __HTIterator<K, T, KeyOfT, hashFunc> iterator;
		//iterator 有两个参数 _node _pht
		iterator begin()
		{
			//找到第一个元素
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					return iterator(_table[i], this);//单参数才支持隐士类型的转化
				}
			}
			//找不到
			return end();
		}

		iterator end()
		{
			return iterator(nullptr, this);//单参数才支持隐士类型的转化
		}

	public:
		~HashTable()
		{
			size_t i = 0;
			for (; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
			
		}
		bool Insert(const T& data)
		{
			KeyOfT kot;//用于获取key的值
			hashFunc hf;//哈希函数,用于获取对应的hash
			if (Find(kot(data)))
				return false;
			//扩容否?
			if (_n == _table.size())
			{
				size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;
				//重新映射
				/*HashTable<K, V> newHT;
				newHT.resize(newSize,nullptr);*/

				vector<Node*> newHT;
				newHT.resize(newSize, nullptr);
				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						/*newHT.Insert(cur->_kv);
						cur = cur->_next;*/

						//优化写法,获取原表的节点,直接链接到newHT
						Node* next = cur->_next;//手动记录下一个cur的下一个节点
						size_t starti = hf(kot(cur->_data));//获取到key以后,再获取key的映射hash
						size_t hashi = starti %= newSize;//获取映射的hashi
						//链接,头插法。
						cur->_next = newHT[hashi];
						newHT[hashi] = cur;

						cur = next;

					}
					_table[i] = nullptr;
				}

				newHT.swap(_table);//需要析构函数
				//原因:vector自己调用析构函数,但是Node节点却不会自己析构,因为其是内置类型的。
			}

			size_t starti = kot(data);
			starti %= _table.size();
			size_t hashi = hf(starti);

			Node* newnode = new Node(data);
			//头插法链接
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			_n++;

			return true;
		}

		Node* Find(const K& key)
		{
			hashFunc hf;
			KeyOfT kot;
			if (_table.size() == 0)
			{
				return nullptr;
			}
			size_t starti = hf(key);
			size_t hashi = starti %= _table.size();

			Node* cur = _table[hashi];//找到key所在的哪个桶

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return cur;
				}
				cur = cur->_next;
			}

			return nullptr;
		}
		bool Erase(const K& key)
		{
			hashFunc hf;

			if (_table.size() == 0)
				return false;

			size_t starti = hf(key);//获取hash,传递的是否类型是不知道的
			size_t hashi = starti %= _table.size();

			Node* cur = _table[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				if (prev == nullptr)//头节点
				{
					_table[hashi] = cur->_next;
					delete cur;
					return true;
				}
				else//非头结点
				{
					prev->_next = cur->_next;
					delete cur;
					return true;
				}
				//更新
				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

	private:
		vector<Node*> _table;
		size_t _n = 0;
	};
}



unordered_set

#include"Hash.h"

namespace bit
{
	template<class K,class hashFunc = DefaultHash<K>>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename Bucket::HashTable<K, K, SetKeyOfT, hashFunc>::iterator iterator;//去类里面的内嵌类型,需要typename

		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}
		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}

	private:
		Bucket::HashTable<K, K,SetKeyOfT,hashFunc> _ht;
	};

	
}

unordered_map

#include"Hash.h"

namespace bit 
{
	template<class K,class V,class hashFunc = DefaultHash<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename Bucket::HashTable<K, pair<K, V>, MapKeyOfT, hashFunc>::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			_ht.end();
		}
		bool insert(const pair<K,V>& kv)
		{
			_ht.Insert(kv);
		}
	private:
		Bucket::HashTable<K, pair<K,V>, MapKeyOfT> _ht;
	};
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值