[C++]20:unorderedset和unorderedmap结构和封装。

unorderedset和unorderedmap结构和封装

  • 一.哈希表:
    • 1.直接定址法:
    • 2.闭散列的开放定址法:
      • 1.基本结构:
      • 2.insert
      • 3.find
      • 4.erase
      • 5.补充:
      • 6.pair<k,v> k的数据类型:
    • 3.开散列的拉链法/哈希桶:
      • 1.基本结构:
      • 2.insert
        • 1.正常插入:
        • 2.考虑扩容
      • 3.find
      • 4.erase
      • 5.数据计算观察:
  • 二.unordered_set和unordered_map的封装:
    • 1.unordered_set
      • 1.基本结构:
      • 2.插入:
      • 3.迭代器:
      • 4.find
      • 5.整体代码:
    • 2.unorder_map
      • 1.基本结构:
      • 2.插入:
      • 3.迭代器:
      • 4.find
      • 5.operator[]重载:
      • 6.整体代码:
      • 7.补充代码:
    • 3.HashTable
      • 1.find查找:
      • 2.迭代器:
        • 1.基本结构:
        • 2.operator++
        • 3.整体代码:
      • 3.插入:
        • 1.bool返回的插入:
        • 2.重载operator[]实现的重载unordered_map独有:

一.哈希表:

1.直接定址法:

1.数据比较集中并且数据都比较小。
2.使用key值作为下标进行数据的存贮。
3.适用于数据比较小并且连续的情况。

字符串中第一个唯一字符

2.闭散列的开放定址法:

1.基本结构:

namespace oper_addres {enum state {Empty,Delete,Exist,};template<class k, class v>struct Hash_Node {Hash_Node(pair<k, v> x = pair<k,v>()):_date(x),_state(Empty){}pair<k, v> _date;state _state;};template<class k, class v>class Hash {public:Hash(size_t n = 10){_hash.resize(n);}private:vector<Hash_Node<k, v>> _hash;size_t _num = 0;};
}

2.insert

1.不存在重复数据:除留余数法,1%10==1 下标1位置放置数值1,4%10=4 下标4位置放置数值4,以此类推。

2.存在重复数据->线性探测->7%10=7 ,下标7位置放置数值7,17%10=7 下标7位置有值就向后放置下标8就放置17,27%10=7下标7位置有值 下标8位置有值下标9放置,++然后取模找到可以放置值的位置结束。

3.如何确定这个位置的值情况?
提供枚举常量 :Empty Delete Exist

4.扩容+数值拷贝:每插入一个值都需要计算负载因子 = 当前插入的数据量/可以插入的size 当这个差值>=0.7就需要进行扩容,新建一个newhashtable大小为当前表的两倍,新的表使用insert插入原来哈希表数据最后交换两个哈希表的vector。

		bool insert(pair<k, v> x){//扩容:size_t tmp = ((_num * 10) / _hash.size());if (tmp >= 7){Hash<k, v>* newhash = new Hash<k, v>(_hash.size() * 2);for (auto e : _hash){newhash->insert(e._date);}_hash.swap(newhash->_hash);}//1.正常插入:插入位置size_t indx = x.first % _hash.size();//2.进行插入:if ((_hash[indx]._state) != Empty || (_hash[indx]._state) == Delete){//向后查找->线性探测:while (_hash[indx]._state == Exist){indx++;indx %= _hash.size();}_hash[indx]._date = x;_hash[indx]._state = Exist;_num++;return true;}_hash[indx]._date = x;_hash[indx]._state = Exist;_num++;return true;}

在这里插入图片描述

3.find

1.find数据传参查找的数据并且返回数据的下标。
2.数据%hashtable.size(),对应下标位置就是这个值返回下标,如果不是线性探测直到空为止。数据不存在返回-1.

			int find(const k& fd){size_t Hashi = fd % _hash.size();if (_hash[Hashi]._state == Exist && _hash[Hashi]._date.first == fd){return Hashi;}else{while (_hash[Hashi]._date.first != fd && _hash[Hashi]._state != Empty){Hashi++;Hashi %= _hash.size();}if (_hash[Hashi]._date.first == fd && _hash[Hashi]._state == Exist)return Hashi;elsereturn -1;}}

4.erase

1.这个地方首先使用find找到对应的下标位置进行返回。
2.找到就修改这个位置的状态为Delete,并且返回true。

bool erase(const k& fd){int hashi = find(fd);if (hashi != -1){_hash[hashi]._state = Delete;return true;}return false;}

5.补充:

size_t size(){return _num;}bool empty(){if (_num == 0)return true;return false;}

1.哈希冲突?
2.数据%hashtable.size()同一个位置的数据然后进行线性探测,线性探测的次数越多哈希冲突越多。哈希冲突越多,效率就越低。
3.当因子大于0.7就考虑进行扩容。

6.pair<k,v> k的数据类型:

1.数据类型的一个转换考虑如何变成下标可以识别的size_t类型。
2.实现仿函数,并且重载operator()
3.特殊类型可以使用类模板的特化。

template<class T>struct transition{size_t operator()(const T& x){return x;}};//1.string -> stringtemplate<>struct transition<string>{size_t operator()(const string& x){//1.可以计算string字符串的asia码的和并且每次*131降低哈希冲突:size_t sum = 0;for (auto& e : x){sum += (e*131);}return sum;}};

在这里插入图片描述

3.开散列的拉链法/哈希桶:

1.开散列,首先对于key值计算出下标位置,具有相同下标位置的值放在同一个子集里面,每一个子集就是一个桶,每一个桶中的元素通过一个单链表连接在一起,每一个链表的头节点由vector保存

1.基本结构:

namespace Hash_bucket{template<class T>struct transition{size_t operator()(const T& x){return x;}};//1.string -> stringtemplate<>struct transition<string>{size_t operator()(const string& x){//1.可以计算string字符串的asia码的和并且每次*131降低哈希冲突:size_t sum = 0;for (auto& e : x){sum += (e * 131);}return sum;}};template<class k, class v>struct Hash_Node {typedef Hash_Node Node;Hash_Node(pair<k, v> x = pair<k, v>()):_date(x),_next(nullptr){}pair<k, v> _date;Node* _next;};//, class trans = transition<k>template<class k, class v, class trans = transition<k>>class Hash {public:typedef Hash_Node<k, v> Node;Hash(size_t n = 10){_hash.resize(n,nullptr);_num = 0;}private:vector<Node*> _hash;size_t _num;};
}

2.insert

1.正常插入:
bool insert(const pair<k, v>& x){//1.正常插入:trans kot;size_t hashi = kot(x.first) % _hash.size();Node* newnode = new Node(x);//1-1:_hash[hashi]==nullptr 直接插入节点:if (_hash[hashi] == nullptr){_hash[hashi] = newnode;_num++;return true;}//1-2:_hash[hashi]!=nullptr 进行单链表的头插:else{newnode->_next = _hash[hashi];_hash[hashi] = newnode;_num++;return true;}return false;}

在这里插入图片描述

2.考虑扩容

1,什么时候需要取进行扩容?
2.当我们的vector<Node*> _hash;每一个下标处都有非空节点就进行扩容。
3.扩容需要考虑原来链表中保存的节点并且考虑进行重新的插入数据。
4.节点数据不需要先delete后new,直接进行简单连接的转移。
5.开头使用find可以帮助我们判断要插入的这个节点之前存不存在。

		bool insert(const pair<k, v>& x){if (find(x.first))return false;trans kot;//1.计算平衡因子:if (_num == _hash.size()){vector<Node*> newhash(_hash.size() * 2, nullptr);for(int i=0;i<_hash.size();i++){Node* cur = _hash[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = kot(cur->_date.first) % (_hash.size()*2);cur->_next = newhash[hashi];newhash[hashi] = cur;cur = next;}_hash[i] = nullptr;}_hash.swap(newhash);}else{//1.正常插入size_t hashi = kot(x.first) % _hash.size();Node* newnode = new Node(x);//1-1:_hash[hashi]==nullptr 直接插入节点:newnode->_next = _hash[hashi];_hash[hashi] = newnode;_num++;return true;}return false;}

3.find

1.%_hash.size()快速确定下标位置。
2.通过cur遍历单链表找到值相同的节点就返回。
3.找不到就返回空节点。


Node* find(const k& fd)
{trans kot;size_t i = kot(fd) % _hash.size();Node* cur = _hash[i];while (cur){if (cur->_date.first == fd)return cur;cur = cur->_next;}return nullptr;
}

4.erase

1.%_hash.size()快速确定下标位置。
2.两个情况:
2-1:_hash[hashi]保存的就是需要删除的节点
2-1:需要删除的节点在单链表中。

bool erase(const k& fd){trans kot;size_t i = kot(fd) % _hash.size();Node* cur = _hash[i];Node* prev = nullptr;while (cur){if (cur->_date.first == fd){//头节点就是需要删除的if (prev == nullptr){_hash[i] = cur->_next;}//在单链表中的节点需要被删除:else{prev->_next = cur->_next;}return true;}prev = cur;cur = cur->_next;}return false;}

5.数据计算观察:

		void Some(){size_t bucketSize = 0;size_t maxBucketLen = 0;size_t sum = 0;double averageBucketLen = 0;for (size_t i = 0; i < _hash.size(); i++){Node* cur = _hash[i];if (cur){++bucketSize;}size_t bucketLen = 0;while (cur){++bucketLen;cur = cur->_next;}sum += bucketLen;if (bucketLen > maxBucketLen){maxBucketLen = bucketLen;}}averageBucketLen = (double)sum / (double)bucketSize;//平衡因子printf("load factor:%lf\n", (double)_num / _hash.size());//表长度:printf("all bucketSize:%d\n",_hash.size());//桶的个数:printf("bucketSize:%d\n", bucketSize);//最长的桶的长度printf("maxBucketLen:%d\n", maxBucketLen);//平均桶长度printf("averageBucketLen:%lf\n\n", averageBucketLen);}

二.unordered_set和unordered_map的封装:

1.unordered_set

1.基本结构:

namespace sfpy {template<class k,class transition = transition<k>>class myunset {public:struct copy_set {const k& operator()(const k& x){return x;}};private:Hash_bucket::Hash<k,k,copy_set,transition> _t;};
}

2.插入:

//1.插入:bool Insert(const k& x){pair<iterator, bool> ret = _t.Insert(x);return ret.second;}bool insert(const k& x){return _t.insert(x);}

3.迭代器:

typedef typename Hash_bucket::Hash<k, k, copy_set, transition>::_iterator iterator;iterator begin(){return _t.find_begin();}iterator end(){return nullptr;}

4.find

//3.find()iterator _find(const k& x){return _t.Find(x);}

5.整体代码:

namespace sfpy {template<class k,class transition = transition<k>>class myunset {public:struct copy_set {const k& operator()(const k& x){return x;}};typedef typename Hash_bucket::Hash<k, k, copy_set, transition>::_iterator iterator;//1.插入:bool Insert(const k& x){pair<iterator, bool> ret = _t.Insert(x);return ret.second;}bool insert(const k& x){return _t.insert(x);}//2.迭代器:iterator begin(){return _t.find_begin();}iterator end(){return nullptr;}//3.find()iterator _find(const k& x){return _t.Find(x);}private:Hash_bucket::Hash<k,k,copy_set,transition> _t;};
}

2.unorder_map

1.基本结构:

namespace sfpy {template<class k , class v , class transition = transition<k>>class myunmap {public:struct copy_map{const k& operator()(const pair<k,v>& x){return x.first;}};private:Hash_bucket::Hash<k,pair<k,v>, copy_map , transition> _t;};
}

2.插入:

//1.插入:bool Insert(const pair<k, v> x){pair<iterator, bool> ret = _t.Insert(x);return ret.second;}bool insert(const pair<k, v> x){return _t.insert(x);}

3.迭代器:

typedef typename Hash_bucket::Hash<k, pair<k, v>, copy_map, transition>::_iterator iterator;//2.迭代器iterator begin(){return _t.find_begin();}iterator end(){return  _t.find_end();}

4.find

iterator _find(const k& x){return _t.Find(x);}

5.operator[]重载:

v& operator[](const k& key){pair<iterator, bool> ret = _t.Insert(make_pair(key,v()));return ret.first->second;}

6.整体代码:

namespace sfpy {template<class k , class v , class transition = transition<k>>class myunmap {public:struct copy_map{const k& operator()(const pair<k,v>& x){return x.first;}};typedef typename Hash_bucket::Hash<k, pair<k, v>, copy_map, transition>::_iterator iterator;//1.插入:bool Insert(const pair<k, v> x){pair<iterator, bool> ret = _t.Insert(x);return ret.second;}bool insert(const pair<k, v> x){return _t.insert(x);}//2.迭代器iterator begin(){return _t.find_begin();}iterator end(){return  _t.find_end();}//3.find()iterator _find(const k& x){return _t.Find(x);}v& operator[](const k& key){pair<iterator, bool> ret = _t.Insert(make_pair(key,v()));return ret.first->second;}private:Hash_bucket::Hash<k,pair<k,v>, copy_map , transition> _t;};
}

7.补充代码:

1.我们知道在unordered_set和unordered_map中key值是不可以被修改的。
2.我们上面的代码是可以修改key值就是一个比较离谱的事情。
3.封装unordered_map和unordered_set对key的类型进行加const。

namespace sfpy {template<class k , class v , class transition = transition<k>>class myunmap {public:struct copy_map{const k& operator()(const pair<const k,v>& x){return x.first;}};typedef typename Hash_bucket::Hash<k, pair<const k, v>, copy_map, transition>::_iterator iterator;//1.插入:bool Insert(const pair<const k, v> x){pair<iterator, bool> ret = _t.Insert(x);return ret.second;}bool insert(const pair<const k, v> x){return _t.insert(x);}//2.迭代器iterator begin(){return _t.find_begin();}iterator end(){return  _t.find_end();}//3.find()iterator _find(const k& x){return _t.Find(x);}v& operator[](const k& key){pair<iterator, bool> ret = _t.Insert(make_pair(key,v()));return ret.first->second;}private:Hash_bucket::Hash<k,pair<const k, v>, copy_map , transition> _t;};
}

3.HashTable

1.map和set去封装同一个哈希表。
2.模板:template<class k , class T, class copy, class trans>
3.copy类重载了operator()做键值的获取。
4.trans是一个类型转换,比如说string转化为一个size_t类型方便下标的使用。

1.find查找:

1.返回查找到的数据的节点或者空指针。
2.数据计算下表并且遍历单链表找到节点返回节点指针。
3.找不到节点就返回nullptr

Node* find(const k& fd){trans up;copy kot;size_t i = up(fd) % _hash.size();Node* cur = _hash[i];while (cur){if ((up(kot(cur->_date))) == up(fd))return cur;cur = cur->_next;}return nullptr;}

2.迭代器:

1.基本结构:

1.迭代器肯定需要封装数据的节点。
2.封装数据的节点够吗?不够!
3.只封装数据的节点重载++在一个单链表的数据还可以,但是进行链表的跳转就无法实现了。
4.考虑封装一个哈希表到迭代器中。
5.迭代器和哈希表会相互封装(上面的类找不到下面的)—>模板+声明放到上面的那个类的上面。

struct unorderediterator{typedef Hash_Node<T> Node;typedef Hash<k, T, copy, trans> HT;typedef unorderediterator<k, T, copy, trans> self;HT* _Hash;Node* _node;unorderediterator(HT* hash, Node* x):_Hash(hash), _node(x){}};
2.operator++

1,情况一:当前节点的下一个不是空直接修改迭代器中节点的内容_node = cur->next
2.情况二:当前节点的下一个是空,求当前单链表所在节点的哈希下表使用节点访问数据进行计算,找到下标之后哈希表向后进行遍历。
2-1:找到一个不是空的就进行_node的修改。
2-2:找不到,表示哈希表一直向后进行遍历到结尾都是空指针。

//主要是要去找节点:self operator++(){trans kot;copy up;//1.情况一:当前节点有下一个节点:if (_node->_next != nullptr)_node = _node->_next;//2.哈希位置的跳转:else{size_t hashi = kot(up(_node->_date)) % _Hash->_hash.size();hashi++;while (hashi < _Hash->_hash.size()){if (_Hash->_hash[hashi]){_node = _Hash->_hash[hashi];break;}hashi++;}if (hashi == _Hash->_hash.size())_node = nullptr;}return *this;}
3.整体代码:
//迭代器:template<class k, class T, class copy, class trans>struct unorderediterator{typedef Hash_Node<T> Node;typedef Hash<k, T, copy, trans> HT;typedef unorderediterator<k, T, copy, trans> self;HT* _Hash;Node* _node;unorderediterator(HT* hash, Node* x):_Hash(hash), _node(x){}T& operator*(){return _node->_date;}T* operator->(){return &(_node->_date);}bool operator!=(const self& bitter)//self* bitter{//比较哈希表中的vector不是iterator?//迭代器==哈希+节点 比较迭代器的地址还是节点的地址?if (this->_node == bitter._node)return false;return true;}//主要是要去找节点:self operator++(){trans kot;copy up;//1.情况一:当前节点有下一个节点:if (_node->_next != nullptr)_node = _node->_next;//2.哈希位置的跳转:else{size_t hashi = kot(up(_node->_date)) % _Hash->_hash.size();hashi++;while (hashi < _Hash->_hash.size()){if (_Hash->_hash[hashi]){_node = _Hash->_hash[hashi];break;}hashi++;}if (hashi == _Hash->_hash.size())_node = nullptr;}return *this;}};

3.插入:

1.bool返回的插入:

1.正常插入:通过key计算下标值。
2.当前Hash[hashi]中已经有数据进行头插操作。
3.当前Hash[hashi]中没有有数据就修改hash[hashi]值。
4.负载因子到1就需要进行扩容操作,负载因子=单链表个数/hash.size()
5.创建一个新的大小为当前哈希表的两倍,遍历原来的链表有key求下标的方法重新进行原来数据的移动,节约了时间,结尾和_node进行哈希表的交换。

bool insert(const T& x){copy kot;trans up;//1.调find函数去查一下当前要插入的数据是否已经存在if (find(kot(x)))return false;//1.计算平衡因子:if (_num == _hash.size()){vector<Node*> newhash(_hash.size() * 2, nullptr);for (int i = 0; i < _hash.size(); i++){Node* cur = _hash[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = up(kot(cur->_date)) % (_hash.size() * 2);cur->_next = newhash[hashi];newhash[hashi] = cur;cur = next;}_hash[i] = nullptr;}_hash.swap(newhash);}else{//1.正常插入size_t hashi = up(kot(x)) % _hash.size();Node* newnode = new Node(x);//1-1:_hash[hashi]==nullptr 直接插入节点:newnode->_next = _hash[hashi];_hash[hashi] = newnode;_num++;return true;}return false;}Node* find(const k& fd){trans up;copy kot;size_t i = up(fd) % _hash.size();Node* cur = _hash[i];while (cur){if ((up(kot(cur->_date))) == up(fd))return cur;cur = cur->_next;}return nullptr;}
2.重载operator[]实现的重载unordered_map独有:

1,重载operator[]一定需要pair<iterator,bool>的插入返回。
2.operator[]不存在就插入,存在就返回value的引用可以进行修改。
3.ret.second 为false表示已经插入过对应的key值。
4.ret.second 为true表示没有插入过对应的key值这一次刚刚插入数据。
5.优化了一个find的查找返回迭代器类型的数据方便pair<iterator,bool>返回。

pair <_iterator, bool> Insert(const T& x){copy kot;trans up;//1.调find函数去查一下当前要插入的数据是否已经存在if (Find(kot(x))._node)return make_pair(Find(kot(x)),false);//1.计算平衡因子:if (_num == _hash.size()){vector<Node*> newhash(_hash.size() * 2, nullptr);for (int i = 0; i < _hash.size(); i++){Node* cur = _hash[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = up(kot(cur->_date)) % (_hash.size() * 2);cur->_next = newhash[hashi];newhash[hashi] = cur;cur = next;}_hash[i] = nullptr;}_hash.swap(newhash);}else{//1.正常插入size_t hashi = up(kot(x)) % _hash.size();Node* newnode = new Node(x);//1-1:_hash[hashi]==nullptr 直接插入节点:newnode->_next = _hash[hashi];_hash[hashi] = newnode;_num++;return make_pair(_iterator(this,newnode), true);}return make_pair(_iterator(this,nullptr),false);}_iterator Find(const k& fd){trans up;copy kot;size_t i = up(fd) % _hash.size();Node* cur = _hash[i];while (cur){if ((up(kot(cur->_date))) == up(fd))return _iterator(this, cur);cur = cur->_next;}return _iterator(this,nullptr);}
#pragma once#include<iostream>
#include<string>
#include<vector>using namespace std;template<class T>
struct transition
{size_t operator()(const T& x){return x;}
};//1.string -> string
template<>
struct transition<string>
{size_t operator()(const string& x){//1.可以计算string字符串的asia码的和并且每次*131降低哈希冲突:size_t sum = 0;for (auto& e : x){sum += (e * 131);}return sum;}
};namespace Hash_bucket
{template<class T>struct Hash_Node {typedef Hash_Node Node;Hash_Node(T x = T()):_date(x),_next(nullptr){}T _date;Node* _next;};//T() ---> int//T() ---> pair<int,int> pair类型://哈希表和迭代器相互包含需要声明迭代器到哈希表的前面://类模板的声明需要模板+struct/class + 类名:template<class k, class T, class copy, class trans>struct unorderediterator;template<class k , class T, class copy, class trans>class Hash {template<class K, class T, class copy, class Hash>friend struct unorderediterator;public:typedef Hash_Node<T> Node;typedef unorderediterator<k, T, copy, trans> _iterator;Hash(size_t n = 10){_hash.resize(n,nullptr);_num = 0;}pair <_iterator, bool> Insert(const T& x){copy kot;trans up;//1.调find函数去查一下当前要插入的数据是否已经存在if (Find(kot(x))._node)return make_pair(Find(kot(x)),false);//1.计算平衡因子:if (_num == _hash.size()){vector<Node*> newhash(_hash.size() * 2, nullptr);for (int i = 0; i < _hash.size(); i++){Node* cur = _hash[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = up(kot(cur->_date)) % (_hash.size() * 2);cur->_next = newhash[hashi];newhash[hashi] = cur;cur = next;}_hash[i] = nullptr;}_hash.swap(newhash);}else{//1.正常插入size_t hashi = up(kot(x)) % _hash.size();Node* newnode = new Node(x);//1-1:_hash[hashi]==nullptr 直接插入节点:newnode->_next = _hash[hashi];_hash[hashi] = newnode;_num++;return make_pair(_iterator(this,newnode), true);}return make_pair(_iterator(this,nullptr),false);}_iterator Find(const k& fd){trans up;copy kot;size_t i = up(fd) % _hash.size();Node* cur = _hash[i];while (cur){if ((up(kot(cur->_date))) == up(fd))return _iterator(this, cur);cur = cur->_next;}return _iterator(this,nullptr);}bool insert(const T& x){copy kot;trans up;//1.调find函数去查一下当前要插入的数据是否已经存在if (find(kot(x)))return false;//1.计算平衡因子:if (_num == _hash.size()){vector<Node*> newhash(_hash.size() * 2, nullptr);for (int i = 0; i < _hash.size(); i++){Node* cur = _hash[i];while (cur){Node* next = cur->_next;// 头插到新表size_t hashi = up(kot(cur->_date)) % (_hash.size() * 2);cur->_next = newhash[hashi];newhash[hashi] = cur;cur = next;}_hash[i] = nullptr;}_hash.swap(newhash);}else{//1.正常插入size_t hashi = up(kot(x)) % _hash.size();Node* newnode = new Node(x);//1-1:_hash[hashi]==nullptr 直接插入节点:newnode->_next = _hash[hashi];_hash[hashi] = newnode;_num++;return true;}return false;}Node* find(const k& fd){trans up;copy kot;size_t i = up(fd) % _hash.size();Node* cur = _hash[i];while (cur){if ((up(kot(cur->_date))) == up(fd))return cur;cur = cur->_next;}return nullptr;}bool erase(const T& fd){trans kot;copy up;size_t i = kot(up(fd)) % _hash.size();Node* cur = _hash[i];Node* prev = nullptr;while (cur){if (up(cur->_date) == fd){if (prev == nullptr){_hash[i] = cur->_next;}else{prev->_next = cur->_next;}return true;}prev = cur;cur = cur->_next;}return false;}void Some(){size_t bucketSize = 0;size_t maxBucketLen = 0;size_t sum = 0;double averageBucketLen = 0;for (size_t i = 0; i < _hash.size(); i++){Node* cur = _hash[i];if (cur){++bucketSize;}size_t bucketLen = 0;while (cur){++bucketLen;cur = cur->_next;}sum += bucketLen;if (bucketLen > maxBucketLen){maxBucketLen = bucketLen;}}averageBucketLen = (double)sum / (double)bucketSize;//平衡因子printf("load factor:%lf\n", (double)_num / _hash.size());//表长度:printf("all bucketSize:%d\n",_hash.size());//桶的个数:printf("bucketSize:%d\n", bucketSize);//最长的桶的长度printf("maxBucketLen:%d\n", maxBucketLen);//平均桶长度printf("averageBucketLen:%lf\n\n", averageBucketLen);}//找开始的节点:_iterator find_begin(){for (int i = 0; i < _hash.size(); i++){if (_hash[i]){return _iterator(this, _hash[i]);}}return  _iterator(this, nullptr);}_iterator find_end(){return  _iterator(this, nullptr);}private://指针数组:vector<Node*> _hash;size_t _num;};//迭代器:template<class k, class T, class copy, class trans>struct unorderediterator{typedef Hash_Node<T> Node;typedef Hash<k, T, copy, trans> HT;typedef unorderediterator<k, T, copy, trans> self;HT* _Hash;Node* _node;unorderediterator(HT* hash, Node* x):_Hash(hash), _node(x){}T& operator*(){return _node->_date;}T* operator->(){return &(_node->_date);}bool operator!=(const self& bitter)//self* bitter{//比较哈希表中的vector不是iterator?//迭代器==哈希+节点 比较迭代器的地址还是节点的地址?if (this->_node == bitter._node)return false;return true;}//主要是要去找节点:self operator++(){trans kot;copy up;//1.情况一:当前节点有下一个节点:if (_node->_next != nullptr)_node = _node->_next;//2.哈希位置的跳转:else{size_t hashi = kot(up(_node->_date)) % _Hash->_hash.size();hashi++;while (hashi < _Hash->_hash.size()){if (_Hash->_hash[hashi]){_node = _Hash->_hash[hashi];break;}hashi++;}if (hashi == _Hash->_hash.size())_node = nullptr;}return *this;}};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/759249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spark面试重点

文章目录 1.简述hadoop 和 spark 的不同点&#xff08;为什么spark更快&#xff09;2.谈谈你对RDD的理解3.简述spark的shuffle过程4. groupByKey和reduceByKey的区别 1.简述hadoop 和 spark 的不同点&#xff08;为什么spark更快&#xff09; Hadoop 和 Spark 是两种用于大数据…

Python将 PDF 转换为 png 图片的教程

将PDF文件转换为PNG图片&#xff1a;Python实现方法 PDF文件因其跨平台和高保真的特性&#xff0c;在文档共享和打印中得到了广泛应用。然而&#xff0c;在某些情况下&#xff0c;我们需要将PDF页面转换为图片格式&#xff0c;例如在不支持PDF格式的平台上展示内容&#xff0c…

snort规则byte_math规则选项详解

byte_math规则选项的主要功能是从待检测的内存中获取指定的数据&#xff0c;并对数据按照要求进行加工处理&#xff0c;得到结果数值&#xff0c;供后续规则选项使用。 规则语法 规则格式 规则样式 byte_math:bytes <nbytes>,offset <offset>,oper <operate…

小程序中实现轮播图左向堆叠

1、效果图&#xff1a; 轮播图左向堆叠 2、封装的组件&#xff1a; my-swiper.wxml <view><view class"tower-swiper" bindtouchend"TowerEnd"><view class"tower-item" wx:for"{{swiperList}}" wx:key"index&q…

mabatis 下

mybatis 原生的API&注解的方式MyBatis-原生的API调用快速入门需求快速入门代码实现 MyBatis-注解的方式操作快速入门需求快速入门代码实现注意事项和说明 mybatis-config.xml配置文件详解说明properties属性settings全局参数定义typeAliases别名处理器typeHandlers类型处理…

几个特殊的控件

目录 一、3个button 1、button 2、linkbutton 3、ImageButton Enabled属性 二、Image控件 1、使用原因 2、使用方式 法一&#xff1a;指明路径 法二&#xff1a;同一目录 3、使用实例 &#xff08;1&#xff09;要求 &#xff08;2&#xff09;操作 三、Typelink和…

SpringBoot自定义Starter:IP计数业务功能开发

🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开心好久好久😎 📚系列专栏:Java全栈,计算机系列(火速更新中) 💭 格言:种一棵树最好的时间是十年前,其次是现在 🏡动动小手,点个关注不迷路,…

每天学习一个Linux命令之nano

每天学习一个Linux命令之nano 在Linux系统中&#xff0c;有许多文本编辑器可供选择&#xff0c;而nano是其中一款简洁易用的编辑器。本篇博客将详细介绍nano命令及其可用的选项&#xff0c;帮助读者更好地使用这个命令。 Nano命令简介 Nano是一个开源的、易于使用的、基于终…

RocketMq 顺序消费、分区消息、延迟发送消息、Topic、tag分类 实战(基本概念) (一)

1、RocketMq基本概念 Topic 消息主题&#xff0c;一级消息类型&#xff0c;通过Topic对消息进行分类。更多信息&#xff0c;请参见Topic与Tag最佳实践。 消息&#xff08;Message&#xff09; 消息队列中信息传递的载体。 Message ID 消息的全局唯一标识&#xff0c;由云消息队…

对https://registry.npm.taobao.org/tyarn的请求失败,原因:证书过期

今天安装tyarn时&#xff0c;报错如下&#xff1a; request to https://registry.npm.taobao.org/tyarn failed, reason: certificate has expired 原来淘宝镜像过期了&#xff0c;需要重新搞一下 记录一下解决过程&#xff1a; 1.查看当前npm配置 npm config list 2.清空…

持续集成平台 01 jenkins 入门介绍

拓展阅读 Devops-01-devops 是什么&#xff1f; Devops-02-Jpom 简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件 代码质量管理 SonarQube-01-入门介绍 项目管理平台-01-jira 入门介绍 缺陷跟踪管理系统&#xff0c;为针对缺陷管理、任务追踪和项目管理的商业…

JAVAEE多线程——锁

文章目录 什么是锁为什么需要锁如何加锁synchorized 的使用synchronized 修饰方法synchronized 修饰代码块 死锁问题那种场景会造成死锁死锁的本质由于内部存在无限循环导致的死锁 死锁的第二种情况哲学家吃饭模型造成死锁的必要条件 什么是锁 首先我们来解释一下什么是锁呢&a…

如何利用MySQL建立覆盖原表的索引优化查询性能

MySQL数据库中&#xff0c;建立合适的索引对于提高查询性能至关重要。然而&#xff0c;在某些情况下&#xff0c;我们可能需要进一步优化查询性能&#xff0c;而覆盖索引&#xff08;Covering Index&#xff09;就是一种有效的方法。本文将介绍什么是覆盖索引以及如何在MySQL中…

SpringBoot整合Xxl-Job

一、下载Xxl-Job源代码并导入本地并运行 Github地址:GitHub - xuxueli/xxl-job: A distributed task scheduling framework.&#xff08;分布式任务调度平台XXL-JOB&#xff09; 中文文档地址:分布式任务调度平台XXL-JOB 1.使用Idea或Eclipse导入 2.执行sql脚本(红色标记…

机器学习_神经网络

文章目录 简介反向传播小结 简介 为了构建神经网络模型&#xff0c;我们需要首先思考大脑中的神经网络是怎样的&#xff1f;每一个神经元都可以被认为是一个处理单元/神经核&#xff0c;它含有许多输入/树突&#xff0c;并且有一个输出/轴突。神经网络是大量神经元相互链接并通…

【笔试】互联网校招技术研发岗 非技术岗笔试准备方向

【笔试】互联网校招技术研发岗笔试准备方向 文章目录 1、研发&#xff1a;软件开发&#xff08;选择&#xff09;2、研发&#xff1a;非技术题&#xff08;选择&#xff09;3、研发&#xff1a;编程题4、产品、运营、项目、视觉、设计、交互5、人力、财务、行政、咨询、管理6、…

计算机网络简答题:复试+期末

文章目录 1.计算机网络的功能:2.计算机网络的分类:3.主机间的通信方式:4.电报交换、报文交换、分组交换的区别:5.计算机网络的性能指标:6.0SI模型和TCP/IP模型:7.通信信通的方式:8.端到端的通信与点到点通信的区别:9.同步通信和异步通信:10.频分复用、时分复用、波分复用和码分…

H5 与 App、网页之间的通信

前言 本文整理工作中 H5 嵌入 Android、iOS 与 PC 网页后&#xff0c;如何与各端通信。&#xff08;提供 H5 端的代码&#xff09; 环境判断 const ua navigator.userAgent.toLowerCase()const isAndroid /android/i.test(ua)const isIos /iphone|ipod|ios/i.test(ua)cons…

笔试题——得物春招实习

开幕式排练 题目描述 导演在组织进行大运会开幕式的排练&#xff0c;其中一个环节是需要参演人员围成一个环形。演出人员站成了一圈&#xff0c;出于美观度的考虑&#xff0c;导演不希望某一个演员身边的其他人比他低太多或者高太多。 现在给出n个参演人员的身高&#xff0c;问…

使用Pygame做一个乒乓球游戏(2)使用精灵重构

本节没有添加新的功能&#xff0c;而是将前面的功能使用精灵类(pygame.sprite.Sprite) 重构。 顺便我们使用图片美化了一下程序。 看到之前的代码&#xff0c;你会发现代码有点混乱&#xff0c;很多地方使用了全局变量(global)。 本节我们将使用类进行重构。 Block(Sprite)…