您的位置:首页 > 游戏 > 游戏 > c++编程(24)——map的模拟实现

c++编程(24)——map的模拟实现

2025/7/7 15:14:16 来源:https://blog.csdn.net/2301_77239666/article/details/141688818  浏览:    关键词:c++编程(24)——map的模拟实现

欢迎来到博主的专栏:c++编程
博主ID:代码小号

文章目录

    • map的底层
      • 红黑树的节点
    • map的模拟实现
      • map的查找与插入
      • map的迭代器

map的底层

map的底层是一个红黑树,关于红黑树的章节博主写在了数据结构专栏当中,因此不再赘述。

template<class key,class T,class keyofT,class valueofT>
class RBtree
{
public:RBtree():_root(nullptr){}typedef RBtreeNode<T> Node;typedef RBtreeIterator<T> iterator;typedef const RBtreeIterator<T> const_iterator;pair<iterator,bool> find(const key& key){Node* cur = _root;while (cur != nullptr){if (key < kot(cur->_data))cur = cur->_left;else if (key > kot(cur->_data))cur = cur->_right;elsereturn make_pair(iterator(_root,cur),true);}return make_pair(iterator(_root,nullptr),false);}pair<iterator,bool> insert(const T& data){Node* newnode = new Node(data);if (_root == nullptr){_root = newnode;_root->_col = BLACK;return make_pair(iterator(_root,_root), true);}Node* cur = _root;Node* parent = _root;while (cur != nullptr){if (kot(newnode->_data) < kot(cur->_data)){parent = cur;cur = cur->_left;}else if (kot(newnode->_data) >kot( cur->_data)){parent = cur;cur = cur->_right;}else{return make_pair(iterator(_root,cur), false);}}if (kot(newnode->_data) > kot(parent->_data)){newnode->_parent = parent;parent->_right = newnode;}else{newnode->_parent = parent;parent->_left = newnode;}cur = newnode;keep_balance(parent, cur);return make_pair(iterator(_root,cur), true);}
//省略private:Node* _root;keyofT kot;valueofT vot;};

为了与标准库中函数原型相同,博主将inseet和find函数的返回类型修改成了pair<iterator,bool>。

红黑树的节点

enum colour
{RED,BLACK
};
template<class T>
struct RBtreeNode
{RBtreeNode(const T& data):_data(data),_right(nullptr),_left(nullptr),_parent(nullptr),_col(RED){}T _data;//值RBtreeNode* _right;//右子树RBtreeNode* _left;//左子树RBtreeNode* _parent;//父节点colour _col;//颜色
};

与在数据结构中写的红黑树不同,为了让红黑树能够同时称为set和map的底层,博主不再使用pair<key,value>作为节点数据的类型,而是使用T作为泛型,这是因为set的层是key型的红黑树,而map是key-value型的红黑树,这两者之间的区别就在于节点存的数据的类型。如果用pair<key,value>作为红黑树的节点数据类型,那就不能适配set,因此采用泛型T。

map的模拟实现

template<class key,class value>
class map
{
public:typedef pair<key, value> value_type;private:RBtree<key, value_type, mapkeyofT,valueofT> _t;
};

map需要key值映射value值,因此需要实例化出pair<key,value>类型的红黑树底层。

template<class key,class T,class keyofT,class valueofT>
class RBtree
{
public:RBtree():_root(nullptr){}
//省略private:Node* _root;keyofT kot;valueofT vot;};

在底层RBtree中,有两个私有成员需要注意,kot是key值提取器,vot是value值提取器,为什么要做出这种设计呢?我们以函数insert为例。

pair<iterator,bool> insert(const T& data)
{Node* newnode = new Node(data);if (_root == nullptr){_root = newnode;_root->_col = BLACK;return make_pair(iterator(_root,_root), true);}Node* cur = _root;Node* parent = _root;while (cur != nullptr){if (kot(newnode->_data) < kot(cur->_data)){parent = cur;cur = cur->_left;}else if (kot(newnode->_data) >kot( cur->_data)){parent = cur;cur = cur->_right;}else{return make_pair(iterator(_root,cur), false);}}if (kot(newnode->_data) > kot(parent->_data)){newnode->_parent = parent;parent->_right = newnode;}else{newnode->_parent = parent;parent->_left = newnode;}cur = newnode;keep_balance(parent, cur);return make_pair(iterator(_root,cur), true);
}

由于RBtree不仅用于map容器,还需要用于set容器,而set容器的_data只有一个key值,因此使用key值进行比较大小非常方便,而map的_data却是pair<key,value>类型,pair类型对象之间是不支持比较大小的(虽然标准库中也允许pair之间的对象比较大小,但却不是key值之间的比较),因此我们需要将data的key值提取出来。

key值提取器定义在map当中,实现如下:

struct mapkeyofT
{const key& operator()(const pair<key,value>& data){return data.first;}
};

这样我们使用kot(data)时,得到的是data当中的first成员,即key值,而vot则是提取data的second,即value值。

struct valueofT
{const value& operator()(const pair<key,value>& data){return data.second;}
};

map的查找与插入

由于RBtree底层已经设计好了insert和find函数,我们只需要复用RBtree中的对应函数就行,这里很简单。

pair<iterator,bool> insert(const value_type& data)
{return _t.insert(data);
}
pair<iterator, bool> find(const key& key)
{return _t.find(key);
}

map的迭代器

红黑树的迭代器设计就比较麻烦了,RBtree的迭代器是一个双向迭代器,支持前进操作operator++,和后退操作operator--,前进与后退的行为与二叉搜索树的中序遍历完全一致。因此map的实现的难点完全在这里(因为红黑树的底层设计在之前的博客就已经完成了)。

struct RBtreeIterator
{typedef RBtreeNode<T> Node;typedef RBtreeIterator<T> Self;typedef T value_type;RBtreeIterator(Node* root=nullptr,Node* node=nullptr):_root(root),_node(node){}Node* _root;Node* _node;
};

RBtree的迭代器需要存储两个值,一个是存储迭代器指向的树_root,另外一个则是存储指向_root的节点_node。

begin()函数返回_root的最左边的节点,这是因为二叉搜索树中序遍历的第一个节点就是此节点,因此我们将其设置成起始位置begin。
在这里插入图片描述
而c++规定,end()返回的迭代器需要不能指向有效节点,那么我们返回一个空节点(nullptr)即可(实际STL库中并非如此)。

至于operator!=等操作,比较简单,博主直接给上代码。

	template<class T>struct RBtreeIterator{typedef RBtreeNode<T> Node;typedef RBtreeIterator<T> Self;typedef T value_type;RBtreeIterator(Node* root=nullptr,Node* node=nullptr):_root(root),_node(node){}const Self& operator=(const RBtreeIterator it)const{_root = it._root;_node = it._node;}value_type& operator*(){return _node->_data;}value_type* operator->(){return &(_node->_data);}Self& begin(){Node* y = _root;while(y && y->_left != nullptr){y = y->_left;}_node = y;return *this;}const Self& begin() const{Node* y = _root;while (y && y->_left != nullptr){y = y->_left;}_node = y;return *this;}Self& end(){_node = nullptr;return *this;}bool operator!=(Self& it)const{return _node != it._node;}Node* _root;Node* _node;};

接下来就是重点的operator++和operator–操作了,前进的操作需要按照中序遍历的规则。因此我们先来顺一下二叉树的中序遍历规律吧。

情况(1),当前节点的右子树存在时。
在这里插入图片描述

根据中序遍历的规则,如果右子树存在,那么下一个遍历的节点就会在右子树的最左节点
在这里插入图片描述

情况2,如果当前节点不存在右子树,且是父节点的左节点。
在这里插入图片描述
那么下一个遍历的节点就会回溯到其父节点。
在这里插入图片描述

情况3,如果当前节点不存在右子树,而且是父节点的右节点
在这里插入图片描述
那么就一直回溯,回溯到当前节点是父节点的左子树的父节点处(有点绕,但确实是这样)。
在这里插入图片描述
代码如下:

	const Self& operator++(){assert(_node);Node* y = nullptr;if (_node->_right != nullptr){y = _node->_right;while (y->_left != nullptr){y = y->_left;}_node = y;}else{Node* parent = _node->_parent;if (parent->_left == _node)_node = parent;else{y = _node;while (parent&&y == parent->_right){y = parent;parent = y->_parent;}_node = parent;}}return RBtreeIterator(_root, _node);}

而后退操作(operator--)则是反过来,我们来看看迭代器是如何实现后退操作的:
情况(1)如果当前节点是空节点(如果是end()返回的迭代器就是空节点)
那么我们就找到搜索二叉树的最右节点。
在这里插入图片描述

情况2,如果当前节点的左子树存在。
在这里插入图片描述
那么我们寻找左子树的最右节点。
在这里插入图片描述
情况(3)当前节点的左子树不存在。
在这里插入图片描述
那么我们就回溯到当前节点是父节点的右子节点的父节点处。
在这里插入图片描述
代码如下:

const Self& operator--()
{Node* y = nullptr;if (_node == nullptr)//end(){y = _root;while (y && y->_right){y = y->_right;}_node = y;}else if (_node->_left!=nullptr){y = _node->_left;while (y->_right != nullptr){y = y->_right;}_node = y;}else{y = _node;Node* parent = y->_parent;while (parent->_left == y){y = parent;parent = y->_parent;}_node = parent;}return *this;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com