仿新浪全站网站源码,松江公司做网站,成都网站建设推广详情,技术支持 优府网络太原网站建设文章目录 map和setmapmap的框架迭代器operator()operator--()operator()和operator!()operator*()operator-() insertbegin()end()operator[] ()map的所有代码#xff1a; set的封装迭代器的封装总结 map和set
通过观察stl的底层我们可以看见#xff0c;map和set是通过红… 文章目录 map和setmapmap的框架迭代器operator()operator--()operator()和operator!()operator*()operator-() insertbegin()end()operator[] ()map的所有代码 set的封装迭代器的封装总结 map和set
通过观察stl的底层我们可以看见map和set是通过红黑树实现的。 通过观察这些typedef就可以看到map和set的封装基本都是套用的红黑树的迭代器来封装实现的所以我们的map和set也可以通过完成的红黑树来进行封装。
map
map的框架 通过看stl的框架我们可以看到set实际传递了两个key但是两个key的重命名是不一样的map也传递了两个key第一个key是key第二个key是pair所以这里我们在搭建框架的时候我们可以根据stl的底层的框架进行搭建。
namespace lyrics
{templateclass K,class Vclass map{public:private:RBTreeK, pairconst K, V _t;};
}根据stl的源代码map的框架可以简化为上面这种。 为什么要传递第一个参数呢 因为虽然只传递一个参数对于set来说没什么影响但是如果我们想用一个红黑树来封装两个容器的话这其实是有影响的因为在比较的时候set确实不会有影响但是map在比较的时候比较逻辑是通过k来比较而并非通过pair来比较还有就是查找的话查找set也可以通过一个模版参数来办到但是对于map来说查找也是通过pair的first来查找的所以这里我们应该传递两个参数第一个参数用于查找和比较第二个参数用于插入。 迭代器
templateclass T, class Ref, class Ptr
struct RBTreeIterator
{typedef RBTreeNodeT Node;typedef RBTreeIteratorT,Ref,Ptr Self;//迭代器Node* _node;Node* _root;RBTreeIterator(Node* node, Node* root) :_node(node), _root(root) {}
};先创建一个迭代器类由于这里我们取不到原本红黑树中的root所以我们这里初始化的时候多传递一个参数在这个迭代器类中多了一个参数root就是用来取原本红黑树中的root的。
operator()
Self operator()
{//要找到当前节点的右子树的最左节点因为遍历到当前节点说明左子树已经走完了if (_node-_right){Node* leftMost _node-_right;while (leftMost-_left)leftMost leftMost-_left;_node leftMost;}//右为空代表这棵树已经访问完了所以应该找到父亲因为当前节点是父亲的左子树//所以左 中 右//下一个访问的节点应该是访问祖先else{Node* cur _node;Node* parent cur-_parent;while (parent cur parent-_right){cur parent;parent cur-_parent;}_node parent;}return *this;
}我们可以通过上面的红黑树来观察假设当前遍历到的节点就是13由于红黑树的遍历是中序遍历所以可是知道当前左子树已经遍历完了所以所以下一个节点只看能在右子树求的是下一个节点肯定是求右子树最小的节点所以就是当前节点的右子树的最左节点。 如果右子树为空我们来看看这种情况假设我们当前节点是66右子树是空的节点再假设当前节点是父亲的右子树如果当前节点是父亲的右子树那么说明当前节点连带父亲的左子树和右子树都已经访问完了所以应该向上进行遍历将当前节点移动到父亲的位置继续向上遍历当当前节点是父亲的左子树的节点的时候就不需要在遍历了因为遍历顺序是左中右所以左的下一个节点就是右就是当前的父亲节点
operator–()
Self operator--()
{//_node是空代表是endif (_node nullptr){//end--则取的就是最右节点也就是整棵树的最大节点Node* rightMost _root;//找到左子树的最右节点while (rightMost rightMost-_right)rightMost rightMost-_right;_node rightMost;}//--是左子树的最右节点else if (_node-_left){Node* rightMost _node-_left;//找到左子树的最右节点while (rightMost-_right) rightMost rightMost-_right;_node rightMost;}//如果左为空else{Node* cur _node;Node* parent cur-_parent;//因为是反过来的所以是右根左while (parent parent-_left cur){cur parent;parent cur-_parent;}_node parent;}return *this;
}–的操作其实是和相似的但是需要注意的是
–的顺序是右中左而不是左中右如果是右中左的话我们假设当前节点是13那么–的话上一个节点就是11–应该是求的是比当前节点小的最大的一个节点所以比当前节点小所以应该是当前节点的左子树又是最大的所以应该是当前节点的左子树的最右的节点。 但是有可能左子树是空树所以我们我们拿上面的那个红黑树的图来举例当节点是22的时候可以看见左子树是空的所以所以这时候应该去找父节点如果当前节点是父节点的左节点那么继续向上找因为是左节点由于是右中左所以左节点访问完之后连同当前节点的父节点和右子树的节点已经访问完了所以应该将当前节点遍历到父节点继续向上找如果当前节点是父节点的右节点的话那么就可以停下来不用找了因为遍历的顺序是右中左当前节点是父节点的右节点说明下一个节点是父节点所以不用找了直接返回父节点就可以了。
注意这里有一个特殊情况当节点是end()的时候需要特殊处理因为对于第一段逻辑来说我们需要去访问node的left这里我们访问空指针的成员已经报错了所以这里要对空指针进行特殊处理 这里就体现出来我们在初始化迭代器的时候传递root的重要性了这里空指针的情况我们直接取最右节点也就是整棵树最大的节点。
operator()和operator!()
//
bool operator!(const Self s) const
{return _node ! s._node;//迭代器比较就是两个指针相不相等
}
//
bool operator(const Self s) const
{return _node s._node;
}operator*()
//返回节点中的data
Ref operator*()
{return _node-_data;//data是T
}operator-()
//pair的迭代器可以访问first和second
Ptr operator-()
{return (_node-_data);
}insert
我们先看看之前的insert的代码 首先可以看见的是我们之前的红黑树是写的比较死的适用的范围很小为了让其更模版话我们可以将实际需要插入的类型用一个模版类型T来表示。
修改之后可以看见这种形式在传参的时候只需要根据是map或者是set来看传递pair还是传递K。 但是我们接着来看红黑树的代码可以发现我们之前写的比较逻辑都是通过pair的比较逻辑去比较的这里就涉及到一个问题我们现在的data换成T了所以之前比较的逻辑就不符合语法了。 可以看看之前的部分的比较逻辑上面的比较逻辑是基于值是pair的时候成立的但是现在我们需要的是能适用于map和set的两个容器的比较所以这里我们必须进行修改最好的办法就是我们需要传递一个类仿函数我们将他以模版参数的形式传递在map和set中写一个类仿函数然后分别传递到红黑树中这里就多出来了第三个模版参数。
namespace lyrics
{templateclass K,class Vclass map{public:private:RBTreeK, pairconst K, VMapKeyOfT _t;};
}这里第三个参数传递的是一个类仿函数所以这里我们先写一个仿函数这里我们写的目的就是要得到map中的pair的first。
struct MapKeyOfT
{const K operator()(const pair K, V kv){return kv.first;}
};这样我们就可以取到map中pair的第一个了。 接下来比较就方便多了我们只需要创建一个对象即可然后调用operator()()即可接下来我们尝试改写一个insert的逻辑
将下面的代码的比较逻辑用这种方式进行修改之后的insert就适用于map和set了但是在我们查看官方文档之后可以看到官方文档中的insert的返回值是pairiterator,bool所以这里我们也应该修改成和官方文档相同的返回值这样有利于我们后面封装operator[] ()所以改完之后整体就是下面的
pairIterator,bool Insert(const T data)
{//根节点是空时判断一下if (_root nullptr){_root new Node(data);_root-_col BLACK;return make_pair(Iterator(_root,_root), true);}//遍历节点,找到插入位置Node* cur _root;Node* parent nullptr;KeyOfT kot;while (cur){//比当前节点大小就去左子树if (kot(cur-_data) kot(data))//这里调用operator(){parent cur;cur cur-_left;}//比当前节点大就去右子树else if (kot(cur-_data) kot(data)){parent cur;cur cur-_right;}//插入失败返回falseelse return make_pair(Iterator(cur, _root), 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* grandparent parent-_parent;if (grandparent-_left parent){Node* uncle grandparent-_right;//叔叔存在为红if (uncle uncle-_col RED){parent-_col uncle-_col BLACK;grandparent-_col RED;//改变指向cur grandparent;parent cur-_parent;}//叔叔不存在或者叔叔存在且为黑else{//单旋加变色if (cur parent-_left){RotateR(grandparent);parent-_col BLACK;grandparent-_col RED;}//双旋else{//双旋RotateL(parent);RotateR(grandparent);//双旋之后cur和parent是交换了所以cur充当根cur-_col BLACK;grandparent-_col RED;}//第一个节点是黑色节点所以可以直接结束break;}}else{Node* uncle grandparent-_left;//叔叔存在为红if (uncle uncle-_col RED){parent-_col uncle-_col BLACK;grandparent-_col RED;//改变指向,向上更新cur grandparent;parent cur-_parent;}//叔叔不存在或者叔叔存在且为黑else{//单旋加变色if (cur parent-_right){RotateL(grandparent);parent-_col BLACK;grandparent-_col RED;}//双旋else{//双旋RotateR(parent);RotateL(grandparent);//双旋之后cur和parent是交换了所以cur充当根cur-_col BLACK;grandparent-_col RED;}//第一个节点是黑色节点所以可以直接结束break;}}}//暴力处理根节点的颜色_root-_col BLACK;//父亲的颜色是黑色直接结束return make_pair(Iterator(newnode, _root), true);
}map的insert
pairiterator,bool Insert(const pairK, V kv)
{return _t.Insert(kv);
}begin()
Iterator Begin()
{Node* leftSub _root;//最左节点就是第一个while (leftSub leftSub-_left ! nullptr)//找到最左节点leftSub leftSub-_left;return Iterator(leftSub, _root);//用最左节点构造一个begin
}
ConstIterator Begin() const
{Node* leftSub _root;//最左节点就是第一个while (leftSub leftSub-_left ! nullptr)//找到最左节点leftSub leftSub-_left;return ConstIterator(leftSub, _root);//用最左节点构造一个begin
}begin需要封装两个一个是const的begin一个是非const的迭代器。 begin很简单begin就是取到最小的如果当前节点不是nullptr直接访问这棵树的最左节点返回最左节点即可。 对map封装
iterator begin()
{return _t.Begin();
}
const_iterator begin()const
{return _t.Begin();
}end()
//用空代表end
ConstIterator End()const
{return ConstIterator(nullptr, _root);//引用nullptr构造一个迭代器
}
Iterator End()
{return Iterator(nullptr, _root);//引用nullptr构造一个迭代器
}end()我们可以直接返回nullptr。
对map进行封装
iterator end()
{return _t.End();
}
const_iterator end()const
{return _t.End();
}operator[] ()
这个我们查看官方文档可以看见 这个operator[] ()是用insert来封装的所以我们接下来不管三七二十一先取出来当前key对应的值
V operator[](const K key)
{pairiterator, bool ret Insert(make_pair(key, V()));//这里第二个参数给缺省值有可能插入成功有可能插入失败return ret.first-second;
}map的所有代码
namespace lyrics
{templateclass K,class Vclass map{struct MapKeyOfT{const K operator()(const pair K, V kv){return kv.first;}};public:typedef typename RBTreeK, pairconst K, V, MapKeyOfT::Iterator iterator;typedef typename RBTreeK, pairconst K, V, MapKeyOfT::ConstIterator const_iterator;//这里底层取的是const key//这里取const上层就不能修改set也可以用两个const迭代器iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin()const{return _t.Begin();}const_iterator end()const{return _t.End();}pairiterator,bool Insert(const pairK, V kv){return _t.Insert(kv);}iterator find(const K key){return _t.Find();}V operator[](const K key){pairiterator, bool ret Insert(make_pair(key, V()));//这里第二个参数给缺省值有可能插入成功有可能插入失败return ret.first-second;}private:RBTreeK, pairconst K, V, MapKeyOfT _t;};
}set的封装
set的封装和map的如出一辙甚至比map的简单
namespace lyrics
{templateclass Kclass set{struct SetKeyOfT{const K operator()(const K key){return key;}};public://因为类模版没有被实例化所以没有被实例化//加上typename就表示等实例化之后去里面将其替换掉typedef typename RBTreeK, const K, SetKeyOfT::Iterator iterator;//第二个K是给给底层的data用的所以data不能修改所以只用给第二个模版参数加上const就可以了typedef typename RBTreeK, const K, SetKeyOfT::ConstIterator const_iterator;iterator begin(){return _t.Begin();//调用RBTree的begin只是套一层壳子而已}const_iterator begin()const{return _t.Begin();//调用RBTree的begin只是套一层壳子而已}iterator end(){return _t.End();}const_iterator end()const{return _t.End();}pairiterator,bool Insert(const K key){return _t.Insert(key);}iterator find(const K key){return _t.Find(key);}private:RBTreeK, const K, SetKeyOfT _t;};
}迭代器的封装
templateclass T, class Ref, class Ptr
struct RBTreeIterator
{typedef RBTreeNodeT Node;typedef RBTreeIteratorT,Ref,Ptr Self;//迭代器Node* _node;Node* _root;//返回节点中的dataRef operator*(){return _node-_data;//data是T}//pair的迭代器可以访问first和secondPtr operator-(){return (_node-_data);}//构造函数,通过节点的指针进行构造RBTreeIterator(Node* node, Node* root) :_node(node), _root(root) {}//bool operator!(const Self s) const{return _node ! s._node;//迭代器比较就是两个指针相不相等}//bool operator(const Self s) const{return _node s._node;}//前置Self operator(){//要找到当前节点的右子树的最左节点因为遍历到当前节点说明左子树已经走完了if (_node-_right){Node* leftMost _node-_right;while (leftMost-_left)leftMost leftMost-_left;_node leftMost;}//右为空代表这棵树已经访问完了所以应该找到父亲因为当前节点是父亲的左子树//所以左 中 右//下一个访问的节点应该是访问祖先else{Node* cur _node;Node* parent cur-_parent;while (parent cur parent-_right){cur parent;parent cur-_parent;}_node parent;}return *this;}Self operator--(){//_node是空代表是endif (_node nullptr){//end--则取的就是最右节点也就是整棵树的最大节点Node* rightMost _root;//找到左子树的最右节点while (rightMost rightMost-_right)rightMost rightMost-_right;_node rightMost;}//--是左子树的最右节点else if (_node-_left){Node* rightMost _node-_left;//找到左子树的最右节点while (rightMost-_right) rightMost rightMost-_right;_node rightMost;}//如果左为空else{Node* cur _node;Node* parent cur-_parent;//因为是反过来的所以是右根左while (parent parent-_left cur){cur parent;parent cur-_parent;}_node parent;}return *this;}
};总结
通过对 map 和 set 的封装我们可以简化代码的使用方式增强数据操作的安全性和可维护性同时根据具体需求扩展功能。这不仅提高了代码的可读性和复用性还在多线程环境下提供了更好的保障。尽管封装需要额外的设计和实现工作但它带来的长远收益是显而易见的。未来我们可以进一步探索封装的优化方向如支持更多类型的容器或集成其他数据结构以适应更复杂的应用场景。在实际项目中合理地利用封装技术将使我们的C开发工作更加高效和灵活。 文章转载自: http://www.morning.wzknt.cn.gov.cn.wzknt.cn http://www.morning.rjjjk.cn.gov.cn.rjjjk.cn http://www.morning.china-cj.com.gov.cn.china-cj.com http://www.morning.xkhhy.cn.gov.cn.xkhhy.cn http://www.morning.lhxdq.cn.gov.cn.lhxdq.cn http://www.morning.qsswb.cn.gov.cn.qsswb.cn http://www.morning.rjhts.cn.gov.cn.rjhts.cn http://www.morning.fwnyz.cn.gov.cn.fwnyz.cn http://www.morning.qxnns.cn.gov.cn.qxnns.cn http://www.morning.xmxbm.cn.gov.cn.xmxbm.cn http://www.morning.zcrjq.cn.gov.cn.zcrjq.cn http://www.morning.grqlc.cn.gov.cn.grqlc.cn http://www.morning.flpjy.cn.gov.cn.flpjy.cn http://www.morning.hytfz.cn.gov.cn.hytfz.cn http://www.morning.hgsylxs.com.gov.cn.hgsylxs.com http://www.morning.tqwcm.cn.gov.cn.tqwcm.cn http://www.morning.xsymm.cn.gov.cn.xsymm.cn http://www.morning.gczqt.cn.gov.cn.gczqt.cn http://www.morning.wfmqc.cn.gov.cn.wfmqc.cn http://www.morning.ktnt.cn.gov.cn.ktnt.cn http://www.morning.cmrfl.cn.gov.cn.cmrfl.cn http://www.morning.skmzm.cn.gov.cn.skmzm.cn http://www.morning.vjdofuj.cn.gov.cn.vjdofuj.cn http://www.morning.dgsx.cn.gov.cn.dgsx.cn http://www.morning.wxwall.com.gov.cn.wxwall.com http://www.morning.jkwwm.cn.gov.cn.jkwwm.cn http://www.morning.mggwr.cn.gov.cn.mggwr.cn http://www.morning.xfjwm.cn.gov.cn.xfjwm.cn http://www.morning.mjtft.cn.gov.cn.mjtft.cn http://www.morning.yqtry.cn.gov.cn.yqtry.cn http://www.morning.hgbzc.cn.gov.cn.hgbzc.cn http://www.morning.llcsd.cn.gov.cn.llcsd.cn http://www.morning.nhlyl.cn.gov.cn.nhlyl.cn http://www.morning.ssqwr.cn.gov.cn.ssqwr.cn http://www.morning.lnbcx.cn.gov.cn.lnbcx.cn http://www.morning.gdljq.cn.gov.cn.gdljq.cn http://www.morning.rhchr.cn.gov.cn.rhchr.cn http://www.morning.bmpjp.cn.gov.cn.bmpjp.cn http://www.morning.hcsqznn.cn.gov.cn.hcsqznn.cn http://www.morning.mhwtq.cn.gov.cn.mhwtq.cn http://www.morning.rszt.cn.gov.cn.rszt.cn http://www.morning.rnqyy.cn.gov.cn.rnqyy.cn http://www.morning.fddfn.cn.gov.cn.fddfn.cn http://www.morning.txkrc.cn.gov.cn.txkrc.cn http://www.morning.c7501.cn.gov.cn.c7501.cn http://www.morning.jrhmh.cn.gov.cn.jrhmh.cn http://www.morning.dbfp.cn.gov.cn.dbfp.cn http://www.morning.qyrnp.cn.gov.cn.qyrnp.cn http://www.morning.fewhope.com.gov.cn.fewhope.com http://www.morning.ggcjf.cn.gov.cn.ggcjf.cn http://www.morning.nxfuke.com.gov.cn.nxfuke.com http://www.morning.nxbsq.cn.gov.cn.nxbsq.cn http://www.morning.mmkrd.cn.gov.cn.mmkrd.cn http://www.morning.wmdqc.com.gov.cn.wmdqc.com http://www.morning.rtpw.cn.gov.cn.rtpw.cn http://www.morning.xsetx.com.gov.cn.xsetx.com http://www.morning.cpqqf.cn.gov.cn.cpqqf.cn http://www.morning.ksqzd.cn.gov.cn.ksqzd.cn http://www.morning.bntfy.cn.gov.cn.bntfy.cn http://www.morning.fbhmn.cn.gov.cn.fbhmn.cn http://www.morning.krdb.cn.gov.cn.krdb.cn http://www.morning.xfrqf.cn.gov.cn.xfrqf.cn http://www.morning.jpdbj.cn.gov.cn.jpdbj.cn http://www.morning.xnpml.cn.gov.cn.xnpml.cn http://www.morning.qhmql.cn.gov.cn.qhmql.cn http://www.morning.bbxbh.cn.gov.cn.bbxbh.cn http://www.morning.qgzmz.cn.gov.cn.qgzmz.cn http://www.morning.dwncg.cn.gov.cn.dwncg.cn http://www.morning.qnypp.cn.gov.cn.qnypp.cn http://www.morning.lfttb.cn.gov.cn.lfttb.cn http://www.morning.dfltx.cn.gov.cn.dfltx.cn http://www.morning.jtszm.cn.gov.cn.jtszm.cn http://www.morning.sjwzz.cn.gov.cn.sjwzz.cn http://www.morning.dtnjr.cn.gov.cn.dtnjr.cn http://www.morning.qyxwy.cn.gov.cn.qyxwy.cn http://www.morning.fllfc.cn.gov.cn.fllfc.cn http://www.morning.phtqr.cn.gov.cn.phtqr.cn http://www.morning.kztts.cn.gov.cn.kztts.cn http://www.morning.hwprz.cn.gov.cn.hwprz.cn http://www.morning.wdprz.cn.gov.cn.wdprz.cn