【c++篇】:解读Set和Map的封装原理--编程中的数据结构优化秘籍

news2024/11/30 17:54:21

✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh–CSDN博客
✨ 文章所属专栏:c++篇–CSDN博客

在这里插入图片描述

文章目录

  • 前言
  • 一.`set`和`map`的初步封装
    • 1.树的节点封装修改
    • 2.`Find()`查找函数
    • 3.红黑树的封装修改
    • 4.set和map的初步封装框架
  • 二.红黑树的迭代器封装
    • 1.基本框架
    • 2.常用成员函数
    • 3.前置`++`函数
    • 4.前置`--`函数
    • 5.红黑树封装中的迭代器函数
    • 6.红黑树封装中的插入函数修改
  • 三.set封装完整实现
    • 1.set的迭代器函数
    • 2.set的插入函数
    • 3.测试
  • 四.map封装完整实现
    • 1.map的迭代器函数
    • 2.map的插入函数
    • 3.map的`operator[]`函数
    • 4.测试
  • 五.完整代码文件
    • 1.`RBTree.h`文件
    • 2.`Set.h`文件
    • 3.`Map.h`文件

前言

在之前的文章中,我们知道,setmap是两种常用的关联容器。他们内部通常使用红黑树来实现高效的查找,插入和删除操作,尽管它们提供了不同的接口函数,但它们依然可以通过共享相同的底层数据结构(也就是同一个红黑树)来实现。下面将详细讲解如何通过改造我们之前的红黑树来实现我们自己的setmap容器。(红黑树的实现在我上一篇文章中有详细讲解,不清楚的可以看我之前的文章)

一.setmap的初步封装

1.树的节点封装修改

首先我们来看一下我们之前的红黑树如何实现节点类的封装:

//节点类封装
template<class K,class V>
class RBTreeNode{
public:
    //构造函数
    RBTreeNode(const pair<K,V>& kv)
    :_left(nullptr)
    ,_right(nullptr)
    ,_parent(nullptr)
    ,_kv(kv)
    ,_col(RED)    
    {}
    
    //成员变量
    RBTree<K,V>* _left;
    RBTree<K,V>* _right;
    RBTree<K,V>* _parent;
    pair<K,V> _kv;
    Colour _col;
};

我们学完set和map应该已经知道,set存储的是一个key值,而map存储的是一个键值对key-value,在上面这段代码中,如果通过这两个模板参数可以实现map,但set却没办法使用,因此,这里节点类的两个模板参数需要使用一个泛型参数来修改,这样就可以实现set和map能够共享一颗树。

修改如下:

//RBTree.h文件

template<class T>
class RBTreeNode {
public:
    //构造函数
    RBTreeNode(const T& data)
    :_left(nullptr)
    ,_right(nullptr)
    ,_parent(nullptr)
    ,_data(data)
    ,_col(RED)
    {}

    //成员变量
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    T _data;
    Colour _col;
};

通过上面的修改为一个模板参数就可以实现共享效果:

set存储的是一个键值key,这里的模板参数T就是键值key的类型

map存储的是一个键值对key-value,这里的模板参数T就是容器pair<key,value> 类型

2.Find()查找函数

在上面对节点封装进行修改后,这里又会产生新的问题,如果我们要通过键值Key来查找对应的节点(也就是Find()函数的参数是键值key,对于set来说直接通过键值key就能找到,但是map中的节点存储的是一个键值对key-value,也就是一个,pair(key,value)对象,直接通过参数key并不能查找,具体可以看下面函数

Node* Find(const K& key){
    Node* cur=_root;
    
    while(cur){
        //对于set来说,_data就是key
        //对于map来说,_data是一个piar(key,value)对象
        if(cur->_data<key){
            cur=cur->_right;
        }
        else if(cur->_data>key){
            cur=cur->_left;
        }
        else{
            return cur;
        }
    }
    
    return nullptr;
}

因此为了解决这个问题,这里需要借助一个仿函数来实现两个不同容器的查找

  • set的仿函数:

    struct SetKeyOfT{
        const K& operator()(const K& key){
            return key;
        }
    };
    
  • map的仿函数:

    struct MapKeyOfT{
        const K& operator()(const pair<K,V>& kv){
            return kv.first;
        }
    };
    
  • Find()函数:

    Node* Find(const K& key){
        Node* cur=_root;
        //对于set来说,这里的KeyOfT就是SetKeyOfT
        //对于map来说,这里的KeyOfT就是MapKeyOfT
        KeyOfT kot;
        
        while(cur){
            if(kot(cur->_data)){
                cur=cur->_right;
            }
            else if(kot(cur->_data)>key){
                cur=cur->_left;
            }
            else{
                return cur;
            }
        }
        
        return nullptr;
    }
    

3.红黑树的封装修改

上面了解完如何实现两个不同容器之间的查找之后,这里就需要开始对原本的红黑树进行封装修改,从Find()函数中我们可以看到,需要一个新的模板参数KeyOfT,用来实现不同容器仿函数的查找功能。

修改如下:

//RBTree.h文件

//增加一个新的模板参数KeyOfT
template<class K,class V,class KeyOfT>
class RBTree{
    typedef RBTreeNode<T> Node;
public:   
    //构造函数
    RBTree()
    :_root(nullptr)
    {}
    
    //Node* Find(const K& key);
    //...其他成员函数
    
private:
    Node* _root;
}

4.set和map的初步封装框架

有了前面三个的基础这里就可以开始对set和map进行初步的封装,set和map的底层都是借用同一个红黑树。

  • set的初步封装框架:

    //Set.h文件
    
    namesapce MySet{
        template<class K>
        class Set{
            //将仿函数设置为内部类
            struct SetKeyOfT{
                const K& operator()(const K& key){
                    return key;
                }
            };
            
        public:
            //...其他成员函数
            
        private:
            //第一个模板参数K是set存储的数据类型
            RBTree<K,K,SetKeyOfT> _t;
        };
    };
    
  • map的初步封装框架:

    //Map.h文件
    
    namesapce MyMap{
        template<class K,class V>
        class Map{
            //将仿函数设置为内部类
            struct MapKeyOfT{
                const K& operator()(const pair<K,V>& kv){
                    return kv.first;
                }
            };
            
        public:
            //...其他成员函数
            
        private:
            //第一个模板参数K是set存储的数据类型
            RBTree<K,pair<const K,V>,MapKeyOfT> _t;
        };
    };
    

二.红黑树的迭代器封装

这里红黑树的迭代器封装其实和容器list比较类似,不能像string和vector一样使用原生指针作为迭代器,只能通过封装结点指针来实现迭代器。

1.基本框架

//迭代器封装
template<class T,class Ptr,class Ref>
class TreeIterator{
    //重命名定义
    typedef RBTreeNode<T> Node;
    typedef TreeIterator<T,Ptr,Ref> self;
    
public:
    //节点指针
    Node* _node;
    
    //构造函数
    TreeIterator(Node* node)
    :_node(node)
    {}
    
    //...成员函数
    
}

2.常用成员函数

  • operator*函数:

    //T& operator*()
    //const T& operator*()
    //用模板参数Ref来实现两个不同返回类型的替换
    Ref opeartor*(){
        return _node->_data;
    }
    
  • operator->函数:

    //T* operator->()
    //const T* operator->()
    //用模板参数Ptr来实现两个不同返回类型的替换
    Ptr operator->(){
        return &_node->_data;
    }
    
  • operator!=函数:

    bool operator!=(const self& s)const {
        return _node!=s._node;
    }
    
  • operator==函数:

    bool operator==(const self& s)const {
        return _node==s._node;
    }
    

3.前置++函数

在这里插入图片描述

self operator++(){
    //如果该节点右子节点不为空,则到右子树中找最左节点
    if(_node->_right){
        Node* subleft=_node->_right;
        
        while(subleft->_left){
            subleft=subleft->_left;
        }
        
        _node=subleft;
    }
    //如果该节点右子节点为空,则找到该节点父节点的左子节点的祖先节点
    else{
        Node* cur=_node;
        Node* parent=cur->_parent;
        
        while(parent){
            //如果cur节点是父节点的右子节点,继续往上
            if(cur==parent->_right){
                cur=parent;
                parent=parent->_parent;
            }
            //如果cur节点是父节点的左子节点,停止
            else{
                break;
            }
        }
        
        _node=parent;
    }
    
    return *this;
}

4.前置--函数

在这里插入图片描述

self& operator--(){
    if(_node->_left){
        Node* subright=_node->_left;
        
        while(subright->_right){
            subright=subright->_right;
        }
        
        _node=subright;
    }
    
    else{
        Node* cur=_node;
        Node* parent=cur->_parent;
        
        while(parent){
            if(cur==parent->_left){
                cur=parent;
                parent=parent->_parent;
            }
            
            else{
                break;
            }
            
            _node=parent;
        }
        
        return *this;
    }
}

5.红黑树封装中的迭代器函数

对于红黑树来说,有普通对象迭代器和const对象迭代器。

template<class ,class T,class KeyOfT>
class RBTree{
    //....
    public:
    //普通对象迭代器
    typedef TreeIterator<T,T*,T&> iterator;
    //const对象迭代器
    typedef TreeIterator<T,const T*,const T&> const_iterator;
    
    iterator begin(){
        Node* cur=_root;
        
        while(cur&&cur->_left){
            cur=cur->_left;
        }
        
        return cur;
    } 
    iterator end(){
        return nullptr;
    }
    
    const_iterator begin()const {
        Node* cur=_root;
        
        while(cur&&cur->_left){
            cur=cur->_left;
        }
        
        return cur;
    }
    const_iterator end()const {
        return nullptr;
    }
}

6.红黑树封装中的插入函数修改

有了前面红黑树封装的迭代器,这里插入函数就可以进行修改,从原本的bool类型,变为pair<iterator,bool>类型,其中,iterator表示插入位置的迭代器,如果差人成功,返回插入位置的迭代器和true;如果该值已经存在,返回该值位置的迭代器和false。

//这里第三个模板参数KeyOfT就可以用到
pair<iteraotr,bool> insert(const T& data){
    if(_root==nullptr){
        _root=new Node(data);
        _root->_col=BLACK;
        return make_pair(_root,true);
    }
    
    Node* parent=nullptr;
    Node* cur=_root;
    
    KeyOfT kot;
    
    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 make_pair(cur,false);
        }
    }
    
    cur=new Node(data);
    cur=_col=RED;
    
    if(kot(parent->_data)<kot(data)){
        parent->_right=cur;
    }
    else{
        parent->_left=cur;
    }
    cur->_parent=parent;
    
    //更新平衡因子,旋转,变色
    //...
    
    return make_pair(newnode,true);
}

三.set封装完整实现

1.set的迭代器函数

在前面通过对红黑树的迭代器进行封装之后,这里就可以直接实现set的迭代器函数

  • 代码实现:
namespace MySet{
    template<class K>
    class set{
        //内部类,用来获取set存储对象中的key值
        struct SetKeyOfT{
            const K& operator()(const K& key){
                return key;
            }
        };

    public:
        //这里的类模板还没有实例化对象,所以要加上关键字typename
        typedef typename RBTree<K,K,SetKeyOfT>::const_iterator iterator;
        typedef typename RBTree<K,K,SetKeyOfT>::const_iterator const_iterator;
        
        //set的begin()函数
        const_iterator begin()const {
            return _t.begin();
        }
        //set的end()函数
        const_iterator end()const {
            return _t.end();
        }

    private:
        //第一个模板参数k是set存储的数据类型
        RBTree<K,K,SetKeyOfT> _t;
    };
};
  • 实现原理:

    • 首先就是要将红黑树原本的迭代器类型进行命名重定义,这里有一个注意点,因为RBTree<K,K,SetKeyOfT>是一个类模板,现在还没有进行实例化,所以直接加上作用域限定符::后面加上迭代器类型会报错,因为编译器并不知道当前模板参数的具体类型,因此要加上关键字typename

    • 其次set还有一个性质就是对于key值,不能进行修改,所以使用迭代器时,如果对于当前迭代器解引用获取key值,要求只能访问,不能修改。因此我们这里可以将set的普通类型迭代器iterator和const类型迭代器const_iterator全都使用红黑树的const类型迭代器typename RBTree<K,K,SetKeyOfT>::const_iterator

2.set的插入函数

set的插入函数返回的是一个pair<iterator,bool>类型

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

注意:set的返回类型pair<iterator,bool>表面上看起来是普通类型的迭代器,但其实,我们是将红黑树的const_iterator迭代器重命名定义成了iterator,因此pair<iterator,bool>中的其实是const_iterator,但是红黑树的插入函数返回的又是一个iterator,所以这里直接写成上面的代码显然是错误的。

正确的写法是要进行一次类型转换:

pair<iterator,bool> insert(const K& key){
    pair<typename RBTree<K,K,SetKeyOfT>::iterator ret=_t.insert(key);
    return pair<iterator,bool>(ret.first,ret.second);
}

为了实现从iterator类型转换为const_iterator,我们要在红黑树的迭代器封装中添加一个构造函数

TreeIterator(Node* node)
:_node(node)
{}

3.测试

测试代码:

#include"Set.h"

int main(){
    MySet::set<int> s;
    s.insert(2);
    s.insert(10);
    s.insert(4);
    s.insert(7);

    MySet::set<int>::iterator sit=s.begin();
    while(sit!=s.end()){
        //(*sit)++;
        //这里修改key值就会报错
        cout<<*sit<<" ";
        ++sit;
    }
    cout<<endl;

    return 0;
}

测试结果:

在这里插入图片描述

四.map封装完整实现

1.map的迭代器函数

这里map的迭代器函数和set的有些不同,因为set要求存储的key值不能被修改,而map只限定了键值对中的key值不能修改,而value值可以修改,所以这里使用红黑树类模板做参数时有些不同,通过pair<const K,V>实现key值不能修改,而value值可以修改。

namespace MyMap{
    template<class K,class V>
    class Map{
        struct MapKeyOfT{
            const K& operator()(const pair<const K,V>& kv){
                return kv.first;
            }
        };

    public:
        
        //map的普通迭代器iterator
        typedef typename RBTree<K,pair<const K,V>,MapKeyOfT>::iterator iterator;
        //map的const类型迭代器const_iterator
        typedef typename RBTree<K,pair<const K,V>,MapKeyOfT>::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();
        }

        //其他成员函数
        //...


    private:
        RBTree<K,pair<const K,V>,MapKeyOfT> _t;

    };
};

2.map的插入函数

相较于set的插入函数,map的插入函数就比较简单,直接调用函数即可

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

3.map的operator[]函数

map相比于set还有一个其他的使用方法,就是[][]可以通过key值,返回value值。如果参数key值不存在map中,会将参数key值插入到map中,然后返回value的对应类型的初始化值,当然还可以通过[]修改对应key值的value值。

V& operator[](const K& key){
    //V()是模板参数V的默认构造
    pair<iterator,bool> rit=insert(make_pair(key,v()));
    return rit.first->second;
}

4.测试

测试代码:

#include"Map.h"

void test1(){
    MyMap::Map<int,int> m;
    m.insert(make_pair(3,3));
    m.insert(make_pair(2,2));
    m.insert(make_pair(1,1));

    MyMap::Map<int,int>::iterator it=m.begin();
    while(it!=m.end()){
        //(it->first)++;
        //这里对key值修改就会报错
        cout<<it->first<<" "<<it->second<<endl;
        ++it;
    }
    cout<<endl;

    m[3]=1;
    m[4];
    m[5]=100;
    for(auto e : m){
        cout<<e.first<<" "<<e.second<<endl;
    }
}

int main(){
    test1();
    return 0;
}

测试结果:

在这里插入图片描述

五.完整代码文件

1.RBTree.h文件

#include<iostream>
#include<utility>
#include<vector>
#include<time.h>
using namespace std;

enum Colour {
    RED,
    BLACK
};

template<class T>
class RBTreeNode {
public:
    //构造函数
    RBTreeNode(const T& data)
    :_left(nullptr)
    ,_right(nullptr)
    ,_parent(nullptr)
    ,_data(data)
    ,_col(RED)
    {}

    //成员变量
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    T _data;
    Colour _col;
};

//迭代器类封装
template<class T,class Ptr,class Ref>
class TreeIterator{
    //重命名定义
    typedef RBTreeNode<T> Node;  
    typedef TreeIterator<T,Ptr,Ref> self;
    typedef TreeIterator<T,T*,T&> Iterator;

public:
    Node* _node;

    TreeIterator(Node* node)
    :_node(node)
    {}

    TreeIterator(const Iterator& it)
    :_node(it._node)
    {}

    Ref operator*(){
        return _node->_data;
    }

    Ptr operator->(){
        return &_node->_data;
    }

    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* subleft=_node->_right;

            while(subleft->_left){
                subleft=subleft->_left;
            }
            _node=subleft;
        }

        //如果该节点右子节点为空,则找到该节点父节点的左子节点的祖先节点
        else{
            Node* cur=_node;
            Node* parent=cur->_parent;

            while(parent){
                //如果cur节点是父节点的右子节点,继续往上
                if(cur==parent->_right){       
                    cur=parent;
                    parent=parent->_parent;
                }

                //如果cur节点是父节点的左子节点,停止
                else{
                    break;
                }
            }

            _node=parent;
        }

        return *this;
    }

    self& operator--(){
        //如果当前节点左子节点不为空,则到该节点的左子树中的最右节点
        if(_node->_left){
            Node* subright=_node->_left;

            while(subright->_right){
                subright=subright->_right;
            }

            _node=subright;
        }
        
        //如果该节点左子节点为空,就到该节点的祖先节点的右子节点
        else{
            Node* cur=_node;
            Node* parent=cur->_parent;

            while(parent){
                if(cur==parent->_left){
                    cur=parent;
                    parent=parent->_parent;
                }

                else{
                    break;
                }
            }

            _node=parent;
        }

        return *this;
    }

};

template<class K , class T, class KeyOfT>
class RBTree {
    typedef RBTreeNode<T> Node;
public:
    //普通对象迭代器
    typedef TreeIterator<T ,T* ,T&> iterator;
    //const对象迭代器
    typedef TreeIterator<T ,const T* ,const T&> const_iterator;

    //构造函数
    RBTree()
    :_root(nullptr)
    {}

    iterator begin(){
        Node* cur=_root;

        while(cur&&cur->_left){
            cur=cur->_left;
        }
        
        return cur;
    }

    iterator end(){
        return nullptr;
    }

    const_iterator begin()const {
        Node* cur=_root;

        while(cur&&cur->_left){
            cur=cur->_left;
        }

        return cur;
    }

    const_iterator end()const {
        return nullptr;
    }

    Node* Find(const K& key){
        Node* cur=_root;
        KeyOfT kot;

        while(cur){
            //这里参数key已经是K类型的,所以不用调用仿函数kot()
            if(kot(cur->_data)<key){                    
                cur=cur->_right;
            }
            else if(kot(cur->_data)>key){
                cur=cur->_left;
            }
            else{
                return cur;
            }
        }

        return nullptr;
    }

    pair<iterator,bool> insert(const T& data) {
        if(_root==nullptr){
            _root=new Node(data);
            _root->_col=BLACK;
            return make_pair(_root,true);
        }

        Node* parent=nullptr;
        Node* cur=_root;

        KeyOfT kot;

        while(cur) {
            //这里参数data是T类型的,是容器里存储的对象,不是K类型,所以要调用仿函数kot()获取key值
            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 make_pair(cur,false);
            }
        }

        cur=new Node(data);
        cur->_col=RED;

        if(kot(parent->_data)<kot(data)){
            parent->_right=cur;
        }
        else{
            parent->_left=cur;
        }
        cur->_parent=parent;

        Node* newnode=cur;

        while(parent&&parent->_col==RED){
            Node* grandfather=parent->_parent;

            //如果parent节点在左子节点
            if(parent==grandfather->_left){
                Node* uncle=grandfather->_right;
                //如果uncle节点存在且节点为红色
                if(uncle&&uncle->_col==RED){
                    //变色
                    parent->_col=uncle->_col=BLACK;
                    grandfather->_col=RED;

                    //继续往上
                    cur=grandfather;
                    parent=cur->_parent;
                }

                //如果uncle节点不存在 或者 节点为黑色
                else{
                    //如果cur节点在左子节点
                    if(cur==parent->_left){
                        //右单旋
                        RotateR(grandfather);

                        //旋转后变色
                        grandfather->_col=RED;
                        parent->_col=BLACK;
                    }

                    //如果cur节点在右子节点
                    else{
                        //左双旋
                        //先左单旋,再右单旋
                        RotateL(parent);
                        RotateR(grandfather);

                        //旋转后变色
                        cur->_col=BLACK;
                        grandfather->_col=RED;
                    }
                    break;
                }
            }

            //如果parent节点在右子节点
            else{
                Node* uncle=grandfather->_left;

                //如果uncle节点存在且节点为红色
                if(uncle&&uncle->_col==RED){
                    //变色
                    parent->_col=uncle->_col=BLACK;
                    grandfather->_col=RED;

                    //继续往上
                    cur=grandfather;
                    parent=cur->_parent;
                }

                //如果uncle节点不存在 后者 节点为黑色
                else{
                    //如果cur节点在右子节点
                    if(cur==parent->_right){
                        //左单旋
                        RotateL(grandfather);

                        //变色
                        parent->_col=BLACK;
                        grandfather->_col=RED;
                    }

                    //如果cur节点在左子节点
                    else{
                        //右双旋
                        //先右单旋,再左单旋
                        RotateR(parent);
                        RotateL(grandfather);

                        //旋转后变色
                        cur->_col=BLACK;
                        grandfather->_col=RED;
                    }
                    break;
                }
            }
        }
        _root->_col=BLACK;

        return make_pair(newnode,true);
    }

    int Height(){
        return _Height(_root);
    }

    bool IsBlance(){
        return _IsBlance(_root);
    }


private:
    int _Height(Node* root){
        if(root==nullptr){
            return 0;
        }

        int leftheight=_Height(root->_left);
        int rightheight=_Height(root->_right);

        return leftheight>rightheight ? leftheight+1 : rightheight+1;
    }

    bool CheckColour(Node* root,int blacknum,int benchmark){
        //如果节点是空,判断黑色节点个数是否等于基准值
        if(root==nullptr){
            if(blacknum!=benchmark){
                return false;
            }
            return true;
        }

        //如果节点是黑色,黑色个数加加
        if(root->_col==BLACK){
            blacknum++;
        }

        //如果节点是红色,判断父节点是否也是红色,不能出现连续的红色节点
        if(root->_col==RED&&root->_parent&&root->_parent->_col==RED){
            cout<<root->_kv.first<<"RED False"<<endl;
            return false;
        }

        return CheckColour(root->_left,blacknum,benchmark)
               &&CheckColour(root->_right,blacknum,benchmark);
    }

    bool _IsBlance(Node* root){
        if(root==nullptr){
            return true;
        }

        //如果根节点不是黑色,返回错误
        if(root->_col!=BLACK){
            return false;
        }

        //设置一个基准值
        int benchmark=0;
        Node* cur=root;
        while(cur){
            if(cur->_col==BLACK){
                benchmark++;
            }
            cur=cur->_left;
        }

        return CheckColour(root,0,benchmark);
    }


    //左单旋
    void RotateL(Node* parent){
        Node* cur=parent->_right;
        Node* curleft=cur->_left;
        Node* ppnode=parent->_parent;

        parent->_right=curleft;
        if(curleft){
            curleft->_parent=parent;
        }

        cur->_left=parent;
        parent->_parent=cur;

        if(ppnode){
            if(ppnode->_left==parent){
                ppnode->_left=cur;
                cur->_parent=ppnode;
            }
            else{
                ppnode->_right=cur;
                cur->_parent=ppnode;
            }
        }
        else{
            cur->_parent=nullptr;
            _root=cur;
        }
    }

    //右单旋
    void RotateR(Node* parent){
        Node* cur=parent->_left;
        Node* curright=cur->_right;
        Node* ppnode=parent->_parent;

        parent->_left=curright;
        if(curright){
            curright->_parent=parent;
        }

        cur->_right=parent;
        parent->_parent=cur;

        if(ppnode){
            if(ppnode->_left==parent){
                ppnode->_left=cur;
                cur->_parent=ppnode;
            }
            else{
                ppnode->_right=cur;
                cur->_parent=ppnode;
            }
        }
        else{
            cur->_parent=nullptr;
            _root=cur;
        }
    }

private:
    Node* _root;
};

2.Set.h文件

#include"RBTree.h"

namespace MySet{
    template<class K>
    class set{
        //内部类,用来获取set存储对象中的key值
        struct SetKeyOfT{
            const K& operator()(const K& key){
                return key;
            }
        };

    public:
        //这里的类模板还没有实例化对象,所以要加上关键字typename
        typedef typename RBTree<K,K,SetKeyOfT>::const_iterator iterator;
        typedef typename RBTree<K,K,SetKeyOfT>::const_iterator const_iterator;

        const_iterator begin()const {
            return _t.begin();
        }

        const_iterator end()const {
            return _t.end();
        }

        //这里返回的是const_iterator类型的迭代器
        pair<iterator,bool> insert(const K& key){
            //插入返回的是iterator类型的迭代器
            pair<typename RBTree<K,K,SetKeyOfT>::iterator,bool> ret=_t.insert(key);
            return pair<iterator,bool>(ret.first,ret.second);
        }

    private:
        //第一个模板参数k是set存储的数据类型
        RBTree<K,K,SetKeyOfT> _t;
    };
};

3.Map.h文件

#include"RBTree.h"

namespace MyMap{
    template<class K,class V>
    class Map{
        struct MapKeyOfT{
            const K& operator()(const pair<const K,V>& kv){
                return kv.first;
            }
        };

    public:

        typedef typename RBTree<K,pair<const K,V>,MapKeyOfT>::iterator iterator;
        typedef typename RBTree<K,pair<const K,V>,MapKeyOfT>::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();
        }

        //operator[],通过key值,返回value值,具备插入和修改
        V& operator[](const K& key){
            pair<iterator,bool> rit=insert(make_pair(key,V()));
            return rit.first->second;
        }

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


    private:
        RBTree<K,pair<const K,V>,MapKeyOfT> _t;

    };
};

以上就是关于set和map的封装讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
在这里插入图片描述

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

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

相关文章

字符型注入‘)闭合

前言 进行sql注入的时候&#xff0c;不要忘记闭合&#xff0c;先闭合再去获取数据 步骤 判断是字符型注入 用order by获取不了显位&#xff0c;select也一样 是因为它是’)闭合&#xff0c;闭合之后&#xff0c;就可以获取数据了 最后就是一样的步骤

电脑启动需要经历哪些过程?

传统BIOS启动流程 1. BIOS BIOS 启动&#xff0c;BIOS程序是烧进主板自带的ROM里的&#xff0c;所以无硬盘也可以启动。BIOS先进行自检&#xff0c;检查内存、显卡、磁盘等关键设备是否存在功能异常&#xff0c;会有蜂鸣器汇报错误&#xff0c;无错误自检飞快结束。 硬件自检…

PYNQ 框架 - OV5640驱动 + Linux 驱动分析

目录 1. 简介 1.1 博文要点 1.2 V4L2 2. 极简 Char 驱动 2.1 源码 2.2 Makefile 2.3 加载驱动 2.4 设备文件 2.5 测试驱动程序 2.6 卸载驱动程序 2.7 自动创建设备文件 2.8 日志等级 3. 极简 V4L2 驱动 3.1 源码 3.2 Makefile 3.3 设备节点类型 3.4 测试 V4L2…

微信小程序Webview与H5通信

背景 近期有个微信小程序需要用到web-view嵌套H5的场景&#xff0c;该应用场景需要小程序中频繁传递数据到H5进行渲染&#xff0c;且需要保证页面不刷新。 由于微信小程序与H5之间的通信限制比较大&#xff0c;显然无法满足于我的业务场景 探索 由于微信小程序与webview的环境是…

【maven-4】IDEA 配置本地 Maven 及如何使用 Maven 创建 Java 工程

IntelliJ IDEA&#xff08;以下简称 IDEA&#xff09;是一款功能强大的集成开发环境&#xff0c;广泛应用于 Java 开发。下面将详细介绍如何在 IDEA 中配置本地 Maven&#xff0c;并创建一个 Maven Java 工程&#xff0c;快速上手并高效使用 Maven 进行 Java 开发。 1. Maven …

交通流量预测:基于交通流量数据建立模型

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【故障处理系列--移动云云盘根目录在线扩容】

移动云云盘根目录扩容 **目的&#xff1a;**测试harbor仓库服务器的根目录能否在线扩容 1、移动云控制台系统盘扩容 这里以我自己的虚拟机演示 2、查看分区的文件类型 3、安装growpart工具 rootgitlab-cli:~# apt install cloud-guest-utils -y rootgitlab-cli:~# apt ins…

不可分割的整体—系统思考的微妙法则

不可分割的整体——系统思考的微妙法则 作为企业领导者&#xff0c;我们经常需要做出决策&#xff0c;但有时候&#xff0c;我们会忽略一个事实&#xff1a;每个决策都不是孤立的&#xff0c;它背后都是一个复杂系统的一部分。 无论是市场动态、团队协作&#xff0c;还是产品…

内核模块里获取当前进程和父进程的cmdline的方法及注意事项,涉及父子进程管理,和rcu的初步介绍

一、背景 在编写内核态系统监控代码时&#xff0c;有时候为了调试的便捷性&#xff0c;不仅要拿到异常事件有关的线程id&#xff0c;进程id和父进程id&#xff0c;还需要拿到当前进程和父进程的comm和cmdline。主要有几下几个原因&#xff1a; 1&#xff09;单纯的pid或者tgi…

京东OPPO定制版 12.1.0 | 安装包只有4.7M,无短视频,适合轻度使用

底部四个标签&#xff0c;没有短视频&#xff0c;可以正常登录。安装包体积很小&#xff0c;功能基本可用&#xff0c;特别适合追求简洁界面和轻度使用的用户。 大小&#xff1a;4.7M 下载地址&#xff1a; 百度网盘&#xff1a;https://pan.baidu.com/s/1lD0o1y9X3s4hRiz-8F…

AOSP的同步问题

repo sync同步时提示出错: error: .repo/manifests/: contains uncommitted changesRepo command failed due to the following UpdateManifestError errors: contains uncommitted changes解决方法&#xff1a; 1、cd 进入.repo/manifests cd .repo/manifests2、执行如下三…

Redis【1】- 如何阅读Redis 源码

1 Redis 的简介 Redis 实际上是简称&#xff0c;全称为 Remote Dictionary Server (远程字典服务器)&#xff0c;由 Salvatore Sanfilippo 写的高性能 key-value 存储系统&#xff0c;其完全开源免费&#xff0c;遵守 BSD 协议。Redis 与其他 key-value 缓存产品&#xff08;如…

鸿蒙修饰符

文章目录 一、引言1.1 什么是修饰符1.2 修饰符在鸿蒙开发中的重要性1.3 修饰符的作用机制 二、UI装饰类修饰符2.1 Styles修饰符2.1.1 基本概念和使用场景2.1.2 使用示例2.1.3 最佳实践 2.2 Extend修饰符2.2.1 基本概念2.2.2 使用示例2.2.3 Extend vs Styles 对比2.2.4 使用建议…

nginx安装和负载均衡

1. nginx安装 &#xff08;1&#xff09;安装依赖项&#xff1a; yum -y install gcc gcc-c make libtool zlib zlib-devel openssl openssl-devel pcre pcre-devel&#xff08;2&#xff09;下载Nginx源代码&#xff1a; http://nginx.org/en/download.html https://nginx.o…

部署 Prometheus

实验环境 IP地址服务192.168.88.10Prometheus服务端, Consul, Grafana, Node-Exporter192.168.88.77MySQL, Node-Exporter192.168.88.30Nginx&#xff0c;Node-Exporter 一、Prometheus Server 端安装和相关配置 【Prometheus1.sh】 &#xff08;1&#xff09;上传 prometh…

通过深度点图表示的隐式场实现肺树结构的高效解剖标注文献速递-生成式模型与transformer在医学影像中的应用

Title 题目 Efficient anatomical labeling of pulmonary tree structures via deeppoint-graph representation-based implicit fields 通过深度点图表示的隐式场实现肺树结构的高效解剖标注 01 文献速递介绍 近年来&#xff0c;肺部疾病&#xff08;Decramer等&#xff…

# 22_ Python基础到实战一飞冲天(二)-python基础(二十二)--名片管理系统案例:cards_tools.py文件

22_ Python基础到实战一飞冲天&#xff08;二&#xff09;-python基础&#xff08;二十二&#xff09;–名片管理系统案例&#xff1a;cards_tools.py文件 一、框架搭建-09-准备名片操作函数修改主文件中函数调用 1、名片管理系统 案例&#xff1a;框架搭建 — cards_tools.p…

Python 和 Pyecharts 对Taptap相关数据可视化分析

结果展示&#xff1a; 数据来源&#xff1a; Python爬取TapTap 热门游戏信息并存储到数据库&#xff08;详细版&#xff09; 目录 结果展示&#xff1a; 数据来源&#xff1a; Python爬取TapTap 热门游戏信息并存储到数据库&#xff08;详细版 一、引言 二、准备工作 三、…

泷羽sec-shell (3)脚本参数传递与数学运算

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

重塑视频新语言,让每一帧都焕发新生——Video-Retalking,开启数字人沉浸式交流新纪元!

模型简介 Video-Retalking 模型是一种基于深度学习的视频再谈话技术&#xff0c;它通过分析视频中的音频和图像信息&#xff0c;实现视频角色口型、表情乃至肢体动作的精准控制与合成。这一技术的实现依赖于强大的技术架构和核心算法&#xff0c;特别是生成对抗网络&#xff0…