当前位置: 首页 > news >正文

set和map简单模拟实现

  • 红黑树
    • 成员变量
    • Insert(第一次修改)
    • Iterator
      • operator++
    • begin、end
    • Insert(第二次修改)
    • Find
    • 构造析构
  • set
    • 成员变量
    • begin、end
    • insert
    • find
  • map
    • 成员变量
    • begin、end
    • insert
    • find
    • operator[]

红黑树

set和map的底层是红黑树,并且前面我们已经实现了。
但还是有点需要修改的地方,首先set是key模型,而map是key_val模型。因此红黑树是否需要实现两套?当然不是,我们只需通过模板操作,传入不同的类型即可实现。

成员变量

template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Color _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};

可以看到我们的模板参数从两个改成了一个,并且根据传入的类型不同转换为不同的模型。
例如set就只需传入key,map就可以传入pair<key,value>。

那么传入的参数不同,那该如何比较大小呢?这就涉及到我们之前讲过的仿函数,我们可以在RBTree里面传入一个仿函数,来获取不同T里面的key:

template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:
private:Node* _root = nullptr;
};

模板参数里面的KeyOfT就是我们想要的仿函数,因此insert被修改成如下:

Insert(第一次修改)

新版Insert:

bool Insert(const T& data)
{if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}KeyOfT kot;//取出T类型的data中的keyNode* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(data);cur->_col = RED; // 新增节点给红色if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//父亲颜色是黑色结束while (parent && parent->_col == RED){//是否一定有爷爷?肯定的,因为父亲结点是红色,因此不是根节点。Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续处理cur = grandfather;//未必有parentparent = cur->_parent;}else//叔叔不存在或者为黑{if (cur == parent->_left){//RotateR(grandfather);Rotate(grandfather->_left);parent->_col = BLACK;grandfather->_col = RED;}else{//RotateL(parent);//RotateR(grandfather);Rotate(parent->_right);Rotate(grandfather->_left);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续处理cur = grandfather;//未必有parentparent = cur->_parent;}else//叔叔不存在或为黑{if (cur == parent->_right){//RotateL(grandfather);Rotate(grandfather->_right);parent->_col = BLACK;grandfather->_col = RED;}else{//RotateR(parent);//RotateL(grandfather);Rotate(parent->_left);Rotate(grandfather->_right);cur->_col = BLACK;grandfather->_col = RED;}break;}}}//处理边界情况_root->_col = BLACK;return true;
}

Rotate:

void Rotate(Node* child)
{Node* parent = child->_parent;Node** childson[] = { &(child->_right),&(child->_left) };Node** parentson[] = { &(parent->_left),&(parent->_right) };bool IsRight = (child == parent->_right);*(parentson[IsRight]) = *(childson[IsRight]);if (*(childson[IsRight]))(*(childson[IsRight]))->_parent = parent;*(childson[IsRight]) = parent;child->_parent = parent->_parent;parent->_parent = child;if (parent == _root){_root = child;child->_parent = nullptr;}else{//Node* grandparent = parent->_parent; 千错万错Node* grandparent = child->_parent;if (parent == grandparent->_left) grandparent->_left = child;else grandparent->_right = child;}}

Iterator

我们知道set和map是有迭代器的,因此我们的底层红黑树需要实现迭代器。根据我们之前写链表迭代器的经验,红黑树迭代器当然也是手到擒来:

template<class T,class Ref,class Ptr>
struct _RBTreeIterator
{typedef RBTreeNode<T> Node;typedef _RBTreeIterator<T, Ref, Ptr> Self;Node* _node;_RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}
};

这样我们就简单实现了operator!=和operator*以及operator->。

operator++

这个迭代器的难点在于如何实现++。
红黑树的迭代器++自然是按照中序的顺序遍历的。

因此对于当前结点来说,比他大的下一个结点就是:右子树的最左结点。
如果右子树为空,并且当前结点为父结点的左子结点,那么比他大的下一个结点就是父结点。
如果右子树为空,并且当前结点为父结点的右子结点,那么就将当前结点改为父结点,重复这三个判断。

具体代码:

Self& operator++()
{//右不为空走右子树最小结点if (_node->_right){Node* rightMin = _node->_right;while (rightMin->_left){rightMin = rightMin->_left;}_node = rightMin;}else{//右为空,如果为左子树,走父亲//如果为右子树就一直向上走。Node* cur = _node;Node* parent = cur->_parent;while (parent&&cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;
}

begin、end

实现了红黑树迭代器的封装,那么我们就能实现红黑树的迭代的一些基本功能:

typedef _RBTreeIterator<T, T&, T*> Iterator;
typedef _RBTreeIterator<T, const T&, const T*> ConstIterator;Iterator Begin()
{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return Iterator(leftMin);
}Iterator End()
{return Iterator(nullptr);
}ConstIterator End() const
{return ConstIterator(nullptr);
}ConstIterator Begin() const
{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return ConstIterator(leftMin);
}

注意到我们的begin返回的是最小结点的迭代器,end返回的是空指针构造的迭代器。

Insert(第二次修改)

有了红黑树的迭代器后,我们就可以对Insert的返回值进行一定处理,应当返回的是pair<Iterator,bool>其中Iterator是插入结点的迭代器,bool为真代表插入成功,反之则插入失败。

pair<Iterator, bool> Insert(const T& data)
{if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return { _root,true };}KeyOfT kot;//取出T类型的data中的keyNode* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return { cur,false };}}cur = new Node(data);Node* newnode = cur;cur->_col = RED; // 新增节点给红色if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//父亲颜色是黑色结束while (parent && parent->_col == RED){//是否一定有爷爷?肯定的,因为父亲结点是红色,因此不是根节点。Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续处理cur = grandfather;//未必有parentparent = cur->_parent;}else//叔叔不存在或者为黑{if (cur == parent->_left){//RotateR(grandfather);Rotate(grandfather->_left);parent->_col = BLACK;grandfather->_col = RED;}else{//RotateL(parent);//RotateR(grandfather);Rotate(parent->_right);Rotate(grandfather->_left);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续处理cur = grandfather;//未必有parentparent = cur->_parent;}else//叔叔不存在或为黑{if (cur == parent->_right){//RotateL(grandfather);Rotate(grandfather->_right);parent->_col = BLACK;grandfather->_col = RED;}else{//RotateR(parent);//RotateL(grandfather);Rotate(parent->_left);Rotate(grandfather->_right);cur->_col = BLACK;grandfather->_col = RED;}break;}}}//处理边界情况_root->_col = BLACK;return { newnode,true };
}

Find

Iterator Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return Iterator(cur);}}return End();
}

构造析构

显然我们的红黑树是需要深拷贝的,已经构造函数:

RBTree() = default;//强制编译器生成默认构造函数RBTree(const RBTree<K, T, KeyOfT>& t)
{_root = Copy(t._root);
}// t2 = t1
RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t)
{swap(_root, t._root);return *this;
}~RBTree()
{Destroy(_root);_root = nullptr;
}Node* Copy(Node* root)
{if (root == nullptr)return nullptr;Node* newroot = new Node(root->_data);newroot->_col = root->_col;newroot->_left = Copy(root->_left);if (newroot->_left)newroot->_left->_parent = newroot;newroot->_right = Copy(root->_right);if (newroot->_right)newroot->_right->_parent = newroot;return newroot;
}void Destroy(Node* root)
{if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;
}

set

成员变量

template<class K>
class set
{struct SetKeyOfT{const K& operator()(const K& key){return key;}};
public://加上typename声明此为内部成员变量,防止不实例化typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;private://防止修改K,传入const KRBTree<K, const K, SetKeyOfT> _t;
};

begin、end

只需要简单的复用即可:

const_iterator begin() const
{return _t.Begin();
}const_iterator end() const
{return _t.End();
}iterator begin()
{return _t.Begin();
}iterator end()
{return _t.End();
}

insert

pair<iterator, bool> insert(const K& key)
{return _t.Insert(key);
}

find

iterator find(const K& key)
{return _t.Find(key);
}

map

成员变量

template<class K,class V>
class map
{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, MapKeyOfT>::ConstIterator const_iterator;
private://防止修改KRBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

begin、end

const_iterator begin() const
{return _t.Begin();
}const_iterator end() const
{return _t.End();
}iterator begin()
{return _t.Begin();
}iterator end()
{return _t.End();
}

insert

pair<iterator, bool> insert(const pair<K, V>& kv)
{return _t.Insert(kv);
}

find

iterator find(const K& key)
{return _t.Find(key);
}

operator[]

map的operator[]显然是一个比较特殊的函数,本质上他是调用一次insert然后返回插入位置的value的引用:

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

事实上我们实现了红黑树的各种功能后,对于set和map的封装就显得轻松异常。

http://www.xdnf.cn/news/661429.html

相关文章:

  • TCP 三次握手过程详解
  • 【Java学习笔记】抽象类
  • 时间的基本概念及相关技术
  • 通用寄存器 专用寄存器
  • 大模型训练中的GPU作用解析
  • 项目三 - 任务8:实现词频统计功能
  • 基于Geotools的Worldpop世界人口tif解析-以中国2020年数据为例
  • 北京大学肖臻老师《区块链技术与应用》公开课:02-BTC-密码学原理
  • Excel快捷键大全
  • 深入理解Java装饰器模式:动态扩展对象功能的优雅之道
  • USB设备状态
  • pyhton基础【5】循环
  • uniapp 小说成品源码
  • Python爬虫实战:研究Selenium框架相关技术
  • NAT、代理服务、内网穿透
  • Python训练营打卡Day37
  • 经典文献阅读之--RT-Grasp(通过MLLM进行推理调优的机器人抓取)
  • 如何设计ES的冷热数据分离架构?Elasticsearch 集群如何实现高可用?如何避免脑裂问题?如果出现脑裂如何恢复?
  • 6.1 Q1|广州医科大学GBD发文 | 良性前列腺增生与合并症之间的相关性
  • mysql ACID 原理
  • OpenCV CUDA模块图像过滤------创建一个 Sobel 滤波器函数createSobelFilter()
  • 高并发下使用防重表做防重案例
  • Linux 常用操作步骤
  • ubantu给github配置ssh
  • Unity—lua基础语法
  • MyBatis-Plus 中 的动态SQL 片段(sqlSegment)讲解
  • 速卖通,国际站测评补单,如何平衡效率和安全
  • C++ ——new和malloc的区别(详细)
  • GROMACS 本地部署教程:模拟生命密码,解码科学未来!
  • 力扣面试150题--二叉搜索树迭代器