C++之set/map相关实现

看着上面的图片,你可能对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;
}

最后,感谢大家的支持!!!

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

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

相关文章

ArkTS开发原生鸿蒙HarmonyOS短视频应用

HarmonyOS实战课程“2024鸿蒙零基础快速实战-仿抖音App开发&#xff08;ArkTS版&#xff09;”已经于今日上线至慕课网&#xff08;https://coding.imooc.com/class/843.html&#xff09;&#xff0c;有致力于鸿蒙生态开发的同学们可以关注一下。 课程简介 本课程以原生鸿蒙Ha…

【Canvas与艺术】新制无底图安布雷拉暗黑系桌面(1920*1080)

【主要变化】 1.去掉底图&#xff0c;改为金丝正六边形组合而成的网格&#xff1b; 2.将安布雷拉标志调暗&#xff1b; 【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html;…

力扣HOT100 - 78. 子集

解题思路&#xff1a; class Solution {public List<List<Integer>> subsets(int[] nums) {List<List<Integer>> lists new ArrayList<>(); // 解集lists.add(new ArrayList<Integer>()); // 首先将空集加入解集中for(int i 0; i < n…

Mac 安装 JDK21 流程

一、下载JDK21 访问Oracle官方网站或选择OpenJDK作为替代品。Oracle JDK从11版本开始是商业的&#xff0c;可能需要支付费用。OpenJDK是一个免费开源选项。 Oracle JDK官方网站&#xff1a;Oracle JDK Downloads OpenJDK官方网站&#xff1a;OpenJDK Downloads 这里以JDK21为…

FP16、BF16、INT8、INT4精度模型加载所需显存以及硬件适配的分析

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

EDA(一)Verilog

EDA&#xff08;一&#xff09;Verilog Verilog是一种用于电子系统设计自动化&#xff08;EDA&#xff09;的硬件描述语言&#xff08;HDL&#xff09;&#xff0c;主要用于设计和模拟电子系统&#xff0c;特别是在集成电路&#xff08;IC&#xff09;和印刷电路板&#xff08;…

CogVLM/CogAgent环境搭建推理测试

引子 对于多模态大语言模型&#xff0c;一直没有怎么接触。刚巧一朋友有问到这方面的问题&#xff0c;也就顺手调研下。智谱AI的东西一直以来&#xff0c;还是很不错的。ChatGLM的忠实fans&#xff0c;看到白嫖网站github上有他们开源的多模态CogVLM/CogAgent&#xff0c;那就…

C语言:文件操作(中)

片头 嗨&#xff01;小伙伴们&#xff0c;大家好&#xff01;在上一篇中&#xff0c;我们学习了C语言&#xff1a;文件操作&#xff08;上&#xff09;&#xff0c;在这一篇中&#xff0c;我们将继续学习文件操作&#xff0c;准备好了吗&#xff1f;Ready Go ! ! ! 文件的顺序…

Linux下top命令指标说明

目录 Linux下top命令指标说明1. 概览2. CPU利用率3. 内存利用率4. 进程信息 Linux下top命令指标说明 在Linux系统中&#xff0c;top 命令是一个用于实时监视系统运行状态的工具。通过 top 命令&#xff0c;我们可以了解系统的负载情况、CPU利用率、内存使用情况以及各个进程的…

ubuntu修改/etc/resolve.conf总是被重置

ubuntu修改/etc/resolve.conf总是被重置 其实处理来很简单&#xff0c;根据英文提示删除/etc/resolve.conf,那是一个软链接&#xff0c;重新创建/etc/resolve.conf rm /etc/resolve.conf vi /etc/resolve.conf 添加nameserver 223.5.5.5

短视频素材去哪里搬运?短视频素材有哪些类型?

在这个数字化和视觉传达至关重要的时代&#xff0c;选择合适的视频素材对于提升视频内容的吸引力和观众参与度至关重要。无论您是一名广告制片人、社交媒体经理还是独立视频制作者&#xff0c;以下这些精选的视频素材网站将为您提供从高清视频到特效资源的全面支持&#xff0c;…

深入解析算法效率核心:时间与空间复杂度概览及优化策略

算法复杂度&#xff0c;即时间复杂度与空间复杂度&#xff0c;衡量算法运行时资源消耗。时间复杂度反映执行时间随数据规模增长的关系&#xff0c;空间复杂度表明额外内存需求。优化策略&#xff0c;如选择合适数据结构、算法改进、循环展开等&#xff0c;对于提升程序效率、减…

如何用 Redis 实现延迟队列?

延迟队列是一种常见的消息队列模式&#xff0c;用于处理需要延迟执行的任务或消息。Redis 是一种快速、开源的键值对存储数据库&#xff0c;具有高性能、持久性和丰富的数据结构&#xff0c;因此很适合用于实现延迟队列。在这篇文章中&#xff0c;我们将详细讨论如何使用 Redis…

树莓派控制步进电机(下):软件编程

目录 说明 软件编程 树莓派的RPI编程 基本测试程序 参考文献 说明 在上一篇博文中我们介绍了树莓派控制步进电机所需要的硬件连接&#xff0c;本篇博文主要介绍软件编程。这里我们使用的是树莓派4B开发板&#xff0c;步进电机为6线两相步进电机&#xff0c;驱动器采用的是…

HTML_CSS学习:背景、鼠标相关属性

一、背景相关属性 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>背景相关属性</title><style>body{background-color: greenyellow;}div{width: 400px;height: …

Java 基础面试 -- 异常处理

一、引言 在Java编程中&#xff0c;异常处理是确保程序稳定性和健壮性的重要机制。当程序在运行时遇到不可预见的问题&#xff0c;如文件读取失败、网络错误、除零异常等&#xff0c;异常处理机制允许我们捕获这些错误&#xff0c;并进行相应的处理&#xff0c;从而避免程序崩…

[实例] Unity Shader 利用顶点着色器模拟简单水波

我们都知道顶点着色器可以用来改变模型各个顶点的位置&#xff0c;那么本篇我们就利用顶点着色器来做一个模拟简单水波的应用。 1. 简谐运动 在进行模拟水波之前&#xff0c;我们需要了解简谐运动&#xff08;Simple Harmonic Motion&#xff09;公式&#xff1a; 其中&#…

A5资源网有哪些类型的资源可以下载?

A5资源网提供了广泛的资源下载&#xff0c;包括但不限于以下类型&#xff1a; 设计素材&#xff1a;包括各类图标、矢量图、背景素材、UI界面元素等&#xff0c;适用于网页设计、平面设计等领域。 图片素材&#xff1a;提供高质量的照片、插图、摄影作品等&#xff0c;可用于…

【文献阅读】 The ITS Irregular Terrain Model(Longely-Rice模型)海上电波传播模型

前言 因为最近在做海上通信的一个项目&#xff0c;所以需要对海上的信道进行建模&#xff0c;所以才阅读到了这一篇文献&#xff0c;下面的内容大部分是我的个人理解&#xff0c;如有错误&#xff0c;请见谅。欢迎在评论区和我一起讨论。 Longely-Rice模型介绍 频率介于 20 …

深入理解Linux内核:访问文件

目录 五种常见的模式 读写文件 从文件中读取数据 函数do_generic_file_read() 普通文件的readpage方法 块设备文件的readpage方法 文件的预读 page_cache_readahead()函数 handle_ra_miss()函数 写入文件 普通文件的prepare_write和commit_write方法 块设备文件的pr…