看着上面的图片,你可能对set和map的多样变化产生疑惑,下面我们就来详细讲解他们的区别以及实现
一.set/map
首先,在这里我们要声明,如果你对二叉搜索树一点都不了解的话,建议你先去将搜索二叉树学会再来学习这里的内容!!!
我也实现过一个二叉搜索树的内容,如下,仅供参考:
数据结构之搜素二叉树-CSDN博客
如果你了解过一些map/set的内容可能会知道,其实实现其是有两种方法的,注意:如果你连map和set是什么都不知道的话,建议Reference - C++ Reference
对于AVLTree实现和红黑树实现,STL中使用红黑树实现的,但是我们两种数据结构都会进行一定的讲解:
AVLTree实现:
#pragma oncetemplate<class K,class V>
struct AVLTreeNode
{//构造函数AVLTreeNode(const pair<K,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0),_kv(kv){}//成员变量AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf;pair<K,V> _kv;
};
template<class K,class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool insert(const pair<K, V>& kv){//两步骤://1.按照搜索二叉树规则插入//特殊if (_root == nullptr){_root = new Node(kv);return true;}Node* root = _root;Node* parent = nullptr;while (root){//注意:比较的是第一个元素if (root->_kv.first < kv.first){parent = root;root = root->_right; }else if (root->_kv.first > kv.first){parent = root;root = root->_left;}else{return false;}}//通过parent来确定插入位置root = new Node(kv);if (parent->_kv.first < kv.first)//右边{parent->_right = root;}else{parent->_left = root;}root->_parent = parent;//步骤二:根据平衡因子来修改AVL树while (parent){if (root == parent->_left)parent->_bf--;elseparent->_bf++;//检查bfif (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){//说明要处理上面root = root->_parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//旋转处理//左单旋if (parent->_bf == 2 && root->_bf == 1){RotateL(parent);}//右单旋else if (parent->_bf == -2 && root->_bf == -1){RotateR(parent);}//左右双旋else if (parent->_bf==-2 && root->_bf == 1 ){RotateLR(parent);}//右左双旋else if (parent->_bf == 2 && root->_bf == -1){RotateRL(parent);}else{return false;}break;//注意点}else{//出现其他情况,说明AVL树有问题return false;}}return true;}int height(){return _height(_root);}//序遍历//中序遍历void Preorder() {_Preorder(_root);}void Inorder(){_Inorder(_root);}void Postorder(){_Postorder(_root);}size_t size(){return _size(_root);}Node* find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_left;}else if (cur->_kv.first > key){cur = cur->_right;}else{return cur;}}return nullptr;}bool balance(){int height = 0;return _balance(_root, height);}
private:bool _balance(Node* root, int& height){if (root == nullptr){height = 0;return true;}int leftheight = 0, rightheight = 0;//三查if (!_balance(root->_left, leftheight) || !_balance(root->_right, rightheight)){return false;}if (abs(rightheight - leftheight) >= 2){cout << root->_kv.first << "不平衡" << endl;return false;}if (rightheight - leftheight != root->_bf){cout << root->_kv.first << "平衡因子异常" << endl;return false;}//重置heightheight = max(leftheight, rightheight) + 1;return true;}size_t _size(Node* root){if (root == nullptr)return 0;return _size(root->_left) + _size(root->_right) + 1;}int _height(Node* root){if (root == nullptr)return 0;int leftsub = _height(root->_left);int rightsub = _height(root->_right);return max(leftsub, rightsub) + 1;}void _Preorder(Node* root){if (root == nullptr)return;cout << root->_kv.first << " " << root->_bf << endl;_Inorder(root->_left);_Inorder(root->_right);}void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << " " << root->_bf << endl;_Inorder(root->_right);}void _Postorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);_Inorder(root->_right);cout << root->_kv.first << " " << root->_bf << endl;}//旋转操作:void RotateL(Node* parent){//保留四个节点:Node* cur = parent->_right;Node* curL = cur->_left;Node* pparent = parent->_parent;//开始改变指向操作parent->_right = curL;if(curL)//注意:需要检查curL是否为空curL->_parent = parent;cur->_left = parent;parent->_parent = cur;//开始检查pparentif (parent == _root){_root = cur;cur->_parent = nullptr;}else{//检查pparent的左右子节点if (pparent->_left == parent){pparent->_left = cur;cur->_parent = pparent;}else{pparent->_right = cur;cur->_parent = pparent;}}//改变_bf值parent->_bf = 0;cur->_bf = 0;}void RotateR(Node* parent){//同理:记录四个子节点Node* cur = parent->_left;Node* curR = cur->_right;Node* pparent = parent->_parent;//操作parent->_left = curR;if (curR)curR->_parent = parent;parent->_parent = cur;cur->_right = parent;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{//检查pparent的左右子节点if (pparent->_left == parent){pparent->_left = cur;cur->_parent = pparent;}else{pparent->_right = cur;cur->_parent = pparent;}}//改变_bf值parent->_bf = 0;cur->_bf = 0;}void RotateLR(Node* parent){//提前保留位置Node* cur = parent->_left;Node* curR = cur->_right;int bf = curR->_bf;//利用已实现的左右旋转来实现RotateL(parent->_left);RotateR(parent);//修改bfif (bf == 0){parent->_bf = 0;cur->_bf = 0;curR->_bf = 0;}else if (bf == 1){cur->_bf = -1;parent->_bf = 0;curR->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;curR->_bf = 0;}else{exit(-1);}}void RotateRL(Node* parent){//提前保留位置Node* cur = parent->_right;Node* curL = cur->_left;int bf = curL->_bf;//利用已实现的右左旋转来实现RotateR(parent->_right);RotateL(parent);//修改bfif (bf == -1){parent->_bf = 0;cur->_bf = 1;curL->_bf = 0;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;curL->_bf = 0;}else if (bf == 0){parent->_bf = 0;cur->_bf = 0;curL->_bf = 0;}else{exit(-1);}}private:Node* _root = nullptr;
};
下面是红黑树的实现:
#pragma onceenum Color
{RED,BLACK
};
template <class T>
struct RBTreeNode
{//成员函数://构造函数:RBTreeNode(const T& date):_left(nullptr),_right(nullptr),_parent(nullptr),_date(date),_color(RED){}//注意点:_color默认为红!!!//成员变量:RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _date;Color _color;
};template<class T,class Ptr,class Ref>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ptr, Ref> Self;RBTreeIterator(Node* node):_node(node){}//解引用Ref operator*(){return _node->_date;}//取地址:Ptr operator->(){return &(_node->_date);}//加减://前置++Self& operator++(){//规则:1.如果有右节点,找右节点的最左节点//2.如果无右节点,找子是父的左树if (_node->_right){//1.有右节点,找右节点的最左节点Node* subright = _node->_right;while (subright->_left){subright = subright->_left;}_node = subright;}else{//2.无右节点,找子是父的左树Node* cur = _node;Node* parent = cur->_parent;while (parent && cur== parent->_right)//防一波root{cur = parent;parent = parent->_parent;}_node = parent;}return *this;}//后置++:Self& operator++(int){Self s = *this;//规则:1.如果有右节点,找右节点的最左节点//2.如果无右节点,找子是父的左树if (_node->_right){//1.有右节点,找右节点的最左节点Node* subright = _node->_right;while (subright->_left){subright = subright->_left;}_node = subright;}else{//2.无右节点,找子是父的左树Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right)//防一波root{cur = parent;parent = parent->_parent;}_node = parent;}return s;}//--操作://前置--:Self& operator--(){//规则:1.如果有左子树节点,找其最右节点//2.如果无左子树节点,找子树是父的右节点位置if (_node->_left){Node* subL = _node->_left;while (subL->_right){subL->_right;}_node = subL;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent == cur->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}//后置--Self& operator--(int){Self s = *this;//规则:1.如果有左子树节点,找其最右节点//2.如果无左子树节点,找子树是父的右节点位置if (_node->_left){Node* subL = _node->_left;while (subL->_right){subL->_right;}_node = subL;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent == cur->_left){cur = parent;parent = parent->_parent;}_node = parent;}return s;}bool operator!=(const Self& s){return !(_node == s._node);}bool operator==(const Self& s){return _node==s._node;}//成员变量:Node* _node;
};template <class K,class T, class KeyOfT>
class RBTree
{
public:typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, T*, T&> iterator;typedef RBTreeIterator<T, const T*, const T&> const_iterator;//迭代器:iterator begin(){Node* subL = _root;//找最左节点while (subL && subL->_left){subL = subL->_left;}return iterator(subL);}iterator end(){return iterator(nullptr);}const_iterator begin()const{Node* subL = _root;while (subL && subL->_left){subL = subL->_left;}return const_iterator(subL);}const_iterator end()const{return const_iterator(nullptr);}pair<iterator, bool> Insert(const T& date){//插入分为两步操作://第一步:插入该节点位置//1.检查_root是否为空if (_root == nullptr){_root = new Node(date);//注意:规则:root节点颜色为黑_root->_color = BLACK;return make_pair(iterator(_root), true);}//2.利用两个Node*,找到要插入位置和其父节点位置Node* cur = _root;Node* parent = nullptr;KeyOfT kot; while (cur){if (kot(cur->_date)<kot(date)){parent = cur;cur = cur->_right;}else if (kot(cur->_date)> kot(date)){parent = cur;cur = cur->_left;}else{//注意:相等报错return make_pair(iterator(cur), false);;}}//3.开空间链接_parentcur = new Node(date);Node* newnode = cur;cur->_parent = parent;if (kot(parent->_date)< kot(cur->_date)){parent->_right = cur;}else{parent->_left = cur;}//第二步:变色+旋转while (parent && parent->_color == RED){//1.找uncleNode* grandparent = parent->_parent;if (parent == grandparent->_left){//分情况讨论:Node* uncle = grandparent->_right;if (uncle && uncle->_color == RED){//1.uncle存在且为红grandparent->_color = RED;uncle->_color = BLACK;parent->_color = BLACK;//注意:这里不需要讨论是否为root情况,原因我们在循环判断中写了parent是否为空情况cur = grandparent;parent = cur->_parent;}else{//2.uncle不存在或者存在且为黑if (cur == parent->_left){//直接右旋RotateR(grandparent);//变色parent->_color = BLACK;grandparent->_color = RED;}else{RotateL(parent);RotateR(grandparent);grandparent->_color = RED;cur->_color = BLACK;}//无论是哪种情况都会结束break;}}else//parent为grandparent右节点{//分情况讨论Node* uncle = grandparent->_left;//1.uncle存在且为红if (uncle && uncle->_color == RED){//变色grandparent->_color = RED;uncle->_color = BLACK;parent->_color = BLACK;//注意:这里不需要讨论是否为root情况,原因我们在循环判断中写了parent是否为空情况cur = grandparent;parent = cur->_parent;}else{//2.uncle可能不存在可能存在且为黑if (cur == parent->_right){//直接左旋RotateL(grandparent);//变色grandparent->_color = RED;parent->_color = BLACK;}else//cur位于左节点{//右左双旋RotateR(parent);RotateL(grandparent);cur->_color = BLACK;grandparent->_color = RED;}//无论是哪种情况都会结束break;}}}_root->_color = BLACK;return make_pair(iterator(newnode), true);}//三序遍历;void Preorder(){_Preorder(_root);}void Inorder(){_Inorder(_root);}void Postorder(){_Postorder(_root);}size_t size(){return _size(_root);}size_t height(){return _height(_root);}iterator find(const K& date){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_date)<date){cur = cur->_right;}else if (kot(cur->_date)>date){cur = cur->_left;}else{return iterator(cur);}}return end();}bool balance(){Node* cur = _root;if (cur && cur->_color == RED){cout << "root节点为红" << endl;return false;}int stantard = 0;while (cur){if (cur->_color == BLACK){stantard++;}cur = cur->_left;}return _balance(_root, 0, stantard);}//查看旋转次数size_t GetRotate(){return rotatesize;}
private:bool _balance(Node* cur, int blacknum, int stantard){if (cur == nullptr){if (blacknum != stantard){cout << "黑节点个数不匹配" << endl;return false;}return true;}//规则二:不能连续出现红节点if (cur->_color == RED && cur->_parent->_color == RED){cout << "连续出现红节点" << endl;return false;}if (cur->_color == BLACK){blacknum++;}return _balance(cur->_left, blacknum, stantard) && _balance(cur->_right, blacknum, stantard);}size_t _height(Node* root){if (root == nullptr)return 0;elsereturn max(_height(root->_left), _height(root->_right)) + 1;}size_t _size(Node* root){if (root == nullptr)return 0;elsereturn _size(root->_left) + _size(root->_right) + 1;}void _Postorder(Node* root){if (root == nullptr)return;_Postorder(root->_left);_Postorder(root->_right);cout << root->_date<< endl;}void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_date << endl;_Inorder(root->_right);}void _Preorder(Node* root){if (root == nullptr)return;cout << root->_date << endl;_Preorder(root->_left);_Preorder(root->_right);}//旋转操作://左旋:void RotateL(Node* parent){rotatesize++;Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;//链接if(subRL)//防止subRL为空情况subRL->_parent = parent;parent->_right = subRL;subR->_left = parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = subR;}else{pparent->_right = subR;}subR->_parent = pparent;}}//右旋:void RotateR(Node* parent){rotatesize++;Node* subL = parent->_left;Node* subLR = subL->_right;Node* pparent = parent->_parent;if (subLR)subLR->_parent = parent;parent->_left = subLR;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = subL;}else{pparent->_right = subL;}subL->_parent = pparent;}}
private:Node* _root = nullptr;size_t rotatesize = 0;
};
我们这里就和系统一样直接套用红黑树实现吗,map和set
如下:
#pragma once#include "RBTree.h"namespace cx
{template<class K>class set{struct KeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, KeyOfT>::iterator iterator;typedef typename RBTree<K, const K, KeyOfT>::const_iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin()const{return _t.begin();}const_iterator end()const{return _t.end();}iterator Find(const K& date){return _t.find(date);}pair<iterator, bool> Insert(const K& date){return _t.Insert(date);}private:RBTree<K, const K, KeyOfT> _t;};
};
#pragma once#include "RBTree.h"namespace cx
{template<class K,class V>class map{struct KeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, KeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, KeyOfT>::const_iterator const_iterator;iterator begin(){return _p.begin();}iterator end(){return _p.end();}const_iterator begin()const{return _p.begin();}const_iterator end()const{return _p.end();}pair<iterator, bool> Insert(const pair<K, V>& kv){return _p.Insert(kv);}iterator Find(const K& date){return _p.find(date);}V& operator[](const K& key){pair<iterator, bool> ret = Insert(make_pair(key, V()));return (ret.first)->second;//ret.first是iterator,iterator的second为( RBTreeIterator<T, T*, T&> iterator)T*//T* 在这里为 <class K,class T, class KeyOfT> 为第二个参数pair<const K, V>//所以这里结果为pair}private:RBTree<K, pair<const K, V>, KeyOfT> _p;};
};
这里我也写过测试用例:
#include <iostream>using namespace std;
#include "map.h"
#include "set.h"
#include "RBTree.h"
using namespace cx;//
//void test_map2()
//{
// string arr[] = { "ƻ", "", "ƻ", "", "ƻ", "ƻ", "",
//"ƻ", "㽶", "ƻ", "", "㽶", "ݮ" };
// map<string, int> countMap;
// for (auto& e : arr)
// {
// /*if (e == "ݮ")
// {
// int i = 0;
// }*/
// countMap[e]++;
// }
//
// for (auto& kv : countMap)
// {
// cout << kv.first << ":" << kv.second << endl;
// }
// cout << endl;
//}//void test_set1()
//{
// set<int> s;
// int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
// for (auto e : a)
// {
// s.insert(e);
// }
//
// set<int>::iterator it = s.begin();
// while (it != s.end())
// {
// //if(*it % 2 == 0)
// // *it += 100;
//
// cout << *it << " ";
// ++it;
// }
// cout << endl;
//}
//}//void test_map1()
//{
// map<string, string> dict;
// dict.insert(pair<string, string>("sort", "排序"));
//
// //pair<string, string> kv("string", "字符串");
// pair<string, string> kv = { "string", "字符串" };
// dict.insert(kv);
//
// // C++11 多参数隐式类型转换(构造函数)
// dict.insert({ "apple", "苹果" });
//
// // C++98
// dict.insert(make_pair("sort", "排序"));
//
// //map<string, string>::iterator it = dict.begin();
// auto it = dict.begin();
// while (it != dict.end())
// {
// //cout << *it << endl;
// //cout << (*it).first << (*it).second << endl;
// cout << it->first << it->second << endl;
// ++it;
// }
// cout << endl;
//
// for (auto& kv : dict)
// {
// cout << kv.first << ":" << kv.second << endl;
// }
// cout << endl;
//}
//
//void test_map2()
//{
// // key相同,value不同,不会插入也不会更新
// map<string, string> dict;
// dict.insert(make_pair("sort", "排序"));
// dict.insert(make_pair("string", "字符串"));
// dict.insert(make_pair("sort", "xxx"));
//
// dict["left"]; // 插入
// cout << dict["sort"] << endl; // 查找
// dict["sort"] = "xxx"; // 修改
// dict["right"] = "右边"; // 插入+修改
//
// for (auto& kv : dict)
// {
// cout << kv.first << ":" << kv.second << endl;
// }
// cout << endl;
//}
//
//void test_map3()
//{
// multimap<string, string> dict;
// dict.insert(make_pair("sort", "排序"));
// dict.insert(make_pair("string", "字符串"));
// dict.insert(make_pair("sort", "xxx"));
// dict.insert(make_pair("sort", "排序"));
//
// for (auto& kv : dict)
// {
// cout << kv.first << ":" << kv.second << endl;
// }
// cout << endl;
//}//
//void test_map4()
//{
// string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
//"苹果", "香蕉", "苹果", "西瓜", "香蕉", "草莓" };
// map<string, int> countMap;
// //for (auto& e : arr)//{// map<string, int>::iterator it = countMap.find(e);// if (it != countMap.end())// {// it->second++;// }// else// {// countMap.insert(make_pair(e, 1));// }//}//for (auto& e : arr)//{// pair<map<string, int>::iterator, bool> ret;// ret = countMap.insert(make_pair(e, 1));// // 已经存在了// if (ret.second == false)// {// ret.first->second++;// }//}// for (auto& e : arr)
// {
// countMap[e]++;
// }
//
// for (auto& kv : countMap)
// {
// //kv.first = "xxx";
// //kv.second = 1;
// cout << kv.first << ":" << kv.second << endl;
// }
// cout << endl;
//}void test_set1()
{cx::set<int> s;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){s.Insert(e);}set<int>::iterator it = s.begin();while (it != s.end()){//if(*it % 2 == 0)// *it += 100;cout << *it << " ";++it;}cout << endl;
}
void test_map1()
{map<int, int> m;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){m.Insert(make_pair(e, e));}map<int, int>::iterator it = m.begin();while (it != m.end()){//it->first += 100;it->second += 100;cout << it->first << ":" << it->second << endl;++it;}cout << endl;
}void test_map2()
{string arr[] = { "ƻ", "", "ƻ", "", "ƻ", "ƻ", "","ƻ", "㽶", "ƻ", "", "㽶", "ݮ" };map<string, int> countMap;for (auto& e : arr){countMap[e]++;}for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}cout << endl;
}
void test_set2()
{set<int> s;s.Insert(3);s.Insert(1);s.Insert(5);s.Insert(7);for (auto e : s){cout << e << " ";}cout << endl;
}
void test_map3()
{string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "西瓜", "香蕉", "草莓" };map<string, int> countMap;for (auto& e : arr){countMap[e]++;}for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}cout << endl;
}
int main()
{//test_map1();//test_set1();//test_map2();//test_set2();test_map3();return 0;
}
二.multimap/multiset
这两个也是用红黑树实现的,所以大家只需要去能清楚和map/set的区别即可!!!
区别如下:
1.multiset中在底层中存储的是的键值对
2. mtltiset的插入接口中只需要插入即可
3. 与set的区别是,multiset中的元素可以重复,set是中value是唯一的
4. 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列
5. multiset中的元素不能修改
6. 在multiset中找某个元素,时间复杂度为$O(log_2 N)$
7. multiset的作用:可以对元素进行排序
8.multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以 重复的。
还需要注意的一点是:
只有map支持[]!!!
三.unordered_set/map
与之前不同这里使用哈希实现的
如果不想出现哈希冲突,我们可以直接定值法,但是容易出现空间消耗过大情况
所以我们更倾向于解决哈希冲突,方法如下:
1.闭散列:
我们可以直接用线性探测解决,也可以用二次探测解决:
实现如下:
#pragma oncenamespace cx_open_address
{//一.哈希冲突线性探测解决问题法:///以下插入、查找、删除操作都只适用int类型:需要表示状态://enum State//{// EMPTY,// EXIST,// DELETE//};哈希数据://template <class K,class V>//class HashDate//{//public:// pair<K, V> _kv;// State _state = EMPTY;//默认为空//};哈希table实现://template<class K,class V>//class Hashtable//{//public:// Hashtable(size_t size = 10)// {// _tables.resize(size);//用resize可以直接先开到size=capacity大小// }// // // //以下插入、查找、删除操作都只适用int类型:// //插入操作:// bool Insert(const pair<K, V>& kv)// {// //检查是否存在// if (Find(kv.first))// return false;// //利用平衡因子解决扩容问题:// //if(_n*1.0/_tables.size()>=0.7)// if (_n * 10 / _tables.size() >= 7)// {// //利用新hashtables来实现扩容// Hashtable<K, V> newtables(_tables.size() * 2);// //遍历原tables,插入到新tables// for (auto& e : _tables)// {// if (e._state == EXIST)// {// newtables.Insert(e._kv);// }// }// //将新tables与原tables交换// _tables.swap(newtables._tables);// }// //正常的线性探测插入操作:// size_t hashi = kv.first % _tables.size();// while (_tables[hashi]._state == EXIST)// {// hashi++;// hashi %= _tables.size();// }// //插入操作:// _tables[hashi]._kv = kv;// _tables[hashi]._state = EXIST;// _n++;//统计的元素个数也增加// return true;// }// HashDate<K, V>* Find(const K& key)// {// //这里是线性探测// //规则:如果当前位置已经有值了,向后查找空位置// size_t hashi = key % _tables.size();//假设现在数据为int// //后面我们在回到string等其他类型// while (_tables[hashi]._state != EMPTY)// {// //当前位置已经存在数据,开始查找// //情况一:如果我们当前位置存的数据就是key// if (key == _tables[hashi]._kv.first &&// _tables[hashi]._state == EXIST)// {// return &_tables[hashi];// }// //情况二:起始位置存的是其他值,现在我们要向后找// hashi++;// //注意:%// hashi %= _tables.size();// }// //如果没位置,返回nullptr// return nullptr;// }// bool Erase(const K& key)// {// //利用Find查找// HashDate<K, V>* ans = Find(key);// //如果存在就删除// if (ans)// {// _n--;// ans->_state = DELETE;// return true;// }// else// {// //不存在,返回false// return false;// }// }//private:// vector<HashDate<K, V>> _tables;// size_t _n = 0;//统计个数//};//下面我们写出适用所有类型的写法://需要表示状态:enum State{EMPTY,EXIST,DELETE};//哈希数据:template <class K, class V>class HashDate{public:pair<K, V> _kv;State _state = EMPTY;//默认为空};template<class K>struct HashFunc{size_t operator()(const K& key){//返回sizereturn (size_t)key;}};//模版特化://string类:template<>struct HashFunc<string>{size_t operator()(const string& s){size_t hash = 0;//for (auto e : s){hash += e;hash *= 131;}return hash;}};//日期类特化:struct Date{int _year;int _month;int _day;};template<>struct HashFunc<Date>{size_t operator()(const Date& d){size_t hash = 0;hash += d._year;hash *= 131;hash += d._month;hash *= 131;hash += d._day;hash *= 131;return hash;}};//pesron类特化:struct Person{string _name;string _id; // 身份证号码string _tel;int _age;string _class;string _address;};template<>struct HashFunc<Person>{size_t operator()(const Person& p){size_t hash = 0;//选择一项进行计算:for (auto e : p._id){hash += e;hash *= 131;}return hash;}};//哈希table实现:template<class K, class V,class Hash=HashFunc<K>>class Hashtable{public:Hashtable(size_t size = 10){_tables.resize(size);//用resize可以直接先开到size=capacity大小}//多类型实现://插入操作:bool Insert(const pair<K, V>& kv){//检查是否存在if (Find(kv.first))return false;//利用平衡因子解决扩容问题://if(_n*1.0/_tables.size()>=0.7)if (_n * 10 / _tables.size() >= 7){//利用新hashtables来实现扩容Hashtable<K, V> newtables(_tables.size() * 2);//遍历原tables,插入到新tablesfor (auto& e : _tables){if (e._state == EXIST){newtables.Insert(e._kv);}}//将新tables与原tables交换_tables.swap(newtables._tables);}//正常的线性探测插入操作:Hash hs;size_t hashi = hs(kv.first) % _tables.size();while (_tables[hashi]._state == EXIST){hashi++;hashi %= _tables.size();}//插入操作:_tables[hashi]._kv = kv;_tables[hashi]._state = EXIST;_n++;//统计的元素个数也增加return true;}HashDate<K, V>* Find(const K& key){//这里是线性探测//规则:如果当前位置已经有值了,向后查找空位置Hash hs;size_t hashi = hs(key) % _tables.size();//假设现在数据为int//后面我们在回到string等其他类型while (_tables[hashi]._state != EMPTY){//当前位置已经存在数据,开始查找//情况一:如果我们当前位置存的数据就是keyif (key == _tables[hashi]._kv.first &&_tables[hashi]._state == EXIST){return &_tables[hashi];}//情况二:起始位置存的是其他值,现在我们要向后找hashi++;//注意:%hashi %= _tables.size();}//如果没位置,返回nullptrreturn nullptr;}bool Erase(const K& key){//利用Find查找HashDate<K, V>* ans = Find(key);//如果存在就删除if (ans){_n--;ans->_state = DELETE;return true;}else{//不存在,返回falsereturn false;}}private:vector<HashDate<K, V>> _tables;size_t _n = 0;//统计个数};/////哈希冲突二次探测解决问题法://与线性探测不同点:不再是一个个向后查找,而是一个数的次方不断增大方式查找空余空间,其余大体相同//这里不进行实现!!!
}
2.开散列:
//不管是线性探测还是二次探测,都是闭散列解决哈希冲突,下面我们实现开散列解决哈希冲突
namespace cx_hash_bucket//cx_close_address
{//每个节点存放的值:template<class K,class V>struct HashNode{HashNode(const pair<K, V>& kv):_next(nullptr), _kv(kv){}HashNode<K, V>* _next;pair<K, V> _kv;};template<class K>struct HashFunc{size_t operator()(const K& key){//返回sizereturn (size_t)key;}};//模版特化://string类:template<>struct HashFunc<string>{size_t operator()(const string& s){size_t hash = 0;//for (auto e : s){hash += e;hash *= 131;}return hash;}};//日期类特化:struct Date{int _year;int _month;int _day;};template<>struct HashFunc<Date>{size_t operator()(const Date& d){size_t hash = 0;hash += d._year;hash *= 131;hash += d._month;hash *= 131;hash += d._day;hash *= 131;return hash;}};//pesron类特化:struct Person{string _name;string _id; // 身份证号码string _tel;int _age;string _class;string _address;};template<>struct HashFunc<Person>{size_t operator()(const Person& p){size_t hash = 0;//选择一项进行计算:for (auto e : p._id){hash += e;hash *= 131;}return hash;}};template<class K,class V,class Hash= HashFunc<K>>class Hashtable{typedef HashNode<K, V> Node;public:Hashtable(const size_t size=10){_tables.resize(size,nullptr);_n = 0;}~Hashtable(){//深拷贝for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}//操作;bool Insert(const pair<K, V>& kv){//同理if (Find(kv.first))return false;//当负载因子达到一扩容Hash hs;if (_n == _tables.size()){vector<Node*> newtables(_tables.size() * 2,nullptr);//遍历原数组for (int i = 0; i < _tables.size(); i++){Node* cur=_tables[i];while (cur){Node* next=cur->_next;//头插到新表size_t hashi = hs(cur->_kv.first) % newtables.size();cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}_tables[i] = nullptr;}//交换_tables.swap(newtables);}//头插插入size_t hashi = hs(kv.first) % _tables.size();Node* newnode = new Node(kv);newnode->_next = _tables[hashi];_tables[hashi] = newnode;_n++;return true;}Node* Find(const K& key){Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (cur->_kv.first == key){return cur;}cur = cur->_next;}return nullptr;}bool Erase(const K& key){Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (cur->_kv.first == key){//删除注意分情况://1.prev为空//2.删除非头if (prev){prev->_next = cur->_next;}else{_tables[hashi] = cur->_next;}delete cur;cur = nullptr;_n--;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _tables;size_t _n = 0;};
}
下面我们利用开散列实现STL中的哈希:
#pragma once
#include "hash.h"namespace cx
{template<class K,class V,class Hash = HashFunc<K>>class myunordered_map{struct MapKeyOfT{const K& operator()(const pair<K,V>& kv){return kv.first;}};public:typedef typename Hashtable<K, const K, MapKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}bool Insert(const pair<K,V>& kv){return _ht.Insert(kv);}bool Erase(const K& key){return _ht.Erase(key);}private:Hashtable<K, pair<const K,V>, MapKeyOfT, Hash> _ht;};
}
#pragma once
#include "hash.h"namespace cx
{template<class K,class Hash= HashFunc<K>>class myunordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename Hashtable<K, const K, SetKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}bool Insert(const K& key){return _ht.Insert(key);}bool Erase(const K& key){return _ht.Erase(key);}private:Hashtable<K, const K, SetKeyOfT, Hash> _ht;};
}
#pragma once//cx_close_address
//每个节点存放的值:
template<class T>
struct HashNode
{HashNode(const T& data):_next(nullptr), _data(data){}HashNode<T>* _next;T _data;
};
template<class K>
struct HashFunc
{size_t operator()(const K& key){//返回sizereturn (size_t)key;}
};
//模版特化:
//string类:
template<>
struct HashFunc<string>
{size_t operator()(const string& s){size_t hash = 0;//for (auto e : s){hash += e;hash *= 131;}return hash;}
};
//日期类特化:
struct Date
{int _year;int _month;int _day;
};
template<>
struct HashFunc<Date>
{size_t operator()(const Date& d){size_t hash = 0;hash += d._year;hash *= 131;hash += d._month;hash *= 131;hash += d._day;hash *= 131;return hash;}
};
//pesron类特化:
struct Person
{string _name;string _id; // 身份证号码string _tel;int _age;string _class;string _address;
};
template<>
struct HashFunc<Person>
{size_t operator()(const Person& p){size_t hash = 0;//选择一项进行计算:for (auto e : p._id){hash += e;hash *= 131;}return hash;}
};
// 前置声明
template<class K, class T, class KeyOfT, class Hash>
class Hashtable;
template<class K, class T, class KeyOfT, class Hash>
struct __HTIterator
{typedef HashNode<T> Node;typedef Hashtable<K, T, KeyOfT, Hash> HT;typedef __HTIterator<K, T, KeyOfT, Hash> Self;//常见操作:__HTIterator(Node* node,HT* ht):_node(node),_ht(ht){}T& operator*(){return _node->_data;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{//当前桶已满,找下一个桶KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();hashi++;while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){_node = _ht->_tables[hashi];break;}hashi++;}if (hashi == _ht->_tables.size()){_node = nullptr;}}return *this;}Self& operator++(int){Self tmp = *this;if (_node->_next){_node = _node->_next;}else{//当前桶已满,找下一个桶KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();hashi++;while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){_node = _ht._tables[hashi];break;}hashi++;}if (hashi == _ht->_tables.size()){_node = nullptr;}}return tmp;}Self& operator--(){KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();if (_ht->_tables[hashi] == _node){//向hashi减小的方向查找Node* cur = nullptr;for (size_t i = hashi - 1; i >= 0; i--){if (_ht->_tables[i]){cur = _ht._tables[i];}}while (cur->_next != nullptr){cur = cur->_next;}_node = cur;}else{Node* cur = _ht->_tables[hashi];Node* prev = nullptr;while (cur != _node){prev = cur;cur = cur->_next;}_node = prev;}return *this;}Self& operator--(int){Self tmp = *this;KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();if (_ht->_tables[hashi] == _node){//向hashi减小的方向查找Node* cur = nullptr;for (size_t i = hashi - 1; i >= 0; i--){if (_ht->_tables[i]){cur = _ht._tables[i];}}while (cur->_next != nullptr){cur = cur->_next;}_node = cur;}else{Node* cur = _ht->_tables[hashi];Node* prev = nullptr;while (cur != _node){prev = cur;cur = cur->_next;}_node = prev;}return tmp;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}Node* _node;HT* _ht;
};
template<class K, class T, class KeyOfT,class Hash>
class Hashtable
{typedef HashNode<T> Node;template<class K, class T, class KeyOfT, class Hash>friend struct __HTIterator;
public:typedef __HTIterator<K, T, KeyOfT, Hash> iterator;iterator begin(){for (size_t i = 0; i < _tables.size(); i++){if (_tables[i]){return iterator(_tables[i], this);}}return end();}iterator end(){return iterator(nullptr, this);}Hashtable(const size_t size = 10){_tables.resize(size, nullptr);_n = 0;}~Hashtable(){//深拷贝for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}//操作;bool Insert(const T& data){KeyOfT kot;if (Find(kot(data)))return false;//当负载因子达到一扩容Hash hs;if (_n == _tables.size()){vector<Node*> newtables(_tables.size() * 2, nullptr);//遍历原数组for (int i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;//头插到新表size_t hashi = hs(kot(cur->_data)) % newtables.size();cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}_tables[i] = nullptr;}//交换_tables.swap(newtables);}//头插插入size_t hashi = hs(kot(data)) % _tables.size();Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;_n++;return true;}Node* Find(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return cur;}cur = cur->_next;}return nullptr;}bool Erase(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (kot(cur->_data)== key){//删除注意分情况://1.prev为空//2.删除非头if (prev){prev->_next = cur->_next;}else{_tables[hashi] = cur->_next;}delete cur;cur = nullptr;_n--;return true;}prev = cur;cur = cur->_next;}return false;}
private:vector<Node*> _tables;size_t _n = 0;
};
测试用例:
#include <iostream>
using namespace std;
#include <string>
#include <vector>//#include "hash.h"
#include "unordered_map.h"
#include "unordered_set.h"void test_set1()
{cx::myunordered_set<int> us;us.Insert(3);us.Insert(1);us.Insert(5);us.Insert(15);us.Insert(45);us.Insert(7);cx::myunordered_set<int>::iterator it = us.begin();while (it != us.end()){//*it += 100;cout << *it << " ";++it;}cout << endl;for (auto e : us){cout << e << " ";}cout << endl;
}
void test_map1()
{cx::myunordered_map<string, string> dict;dict.Insert(make_pair("sort", ""));dict.Insert(make_pair("left", ""));dict.Insert(make_pair("right", "ұ"));
}
int main()
{//test_set1();test_map1();return 0;
}
最后,感谢大家的支持!!!