C++必修:set与map的模拟实现

news2024/12/23 10:29:50

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++学习
贝蒂的主页:Betty’s blog

1. set与map的结构

我们知道STL中的setmap底层就是一颗红黑树,接下来我们模拟setmap肯定需要一颗红黑树作为我们的成员变量,如果在此之前并不了解红黑树,可以参考——红黑树。

1.1 set的结构

我们知道set其实就是K模型,所以set容器对红黑树的封装如下:

template<class K>
class set
{
public:
    //成员函数
private:
    RBTree<K, K> _t;
};

1.2 map的结构

我们也知道map其实就是KV模型,所以map容器对红黑树的封装如下:

template<class K, class V>
class map
{
public:
    //成员函数
private:
    RBTree<K, pair<const K, V>> _t;
};

其中为了防止pairK被修改,我们可以加上const修饰。其实set中的K同样不能修改,但是我们可以在其迭代器实现中进行统一处理。

2. 改造红黑树

其中红黑树的接口如下:

template<class K, class T>
class RBTree

第一个参数代表key,第二个参数为key或者keyvalue形成的键值对。对于set来说,第一个模版参数与第二个模版参数相同;对于map来说,第一个参数为key,第二个参数为keyvalue形成的键值对。

2.1 改造红黑树的节点

其中红黑树的节点类型就是模版参数T,所以我们对节点改造如下:

enum _col
{
	RED,//红色
	BLACK//黑色
};
template<class T>
struct RBNode
{
	RBNode(const T& value = T(), _col col = RED)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _value(value)
		, _col(col)//默认为红节点
	{}
	RBNode<T>* _left;//左子树
	RBNode<T>* _right;//右子树
	RBNode<T>* _parent;//父节点
	T _value;//键值
	_col _col;//颜色
};

2.2 改造红黑树

2.2.1 增加仿函数

首先为了方便不同类型之间的比较,所以我们需要定义一个仿函数,而不同类型需要的仿函数可能是不同的。所以我们需要模版参数显示传参:

template<class K>
class set
{
    //增加仿函数
	struct SetKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
	//成员函数
private:
	RBTree<K, K, SetKeyOfT> _t;
};
template<class K, class V>
class map
{
    //仿函数
    struct MapKeyOfT
    {
        const K& operator()(const pair<K, V>& kv)
        {
            return kv.first;
        }
    };
public:
    //成员函数
private:
    RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

然后我们将所有需要比较key的函数利用仿函数进行替换,我们以Find为例。

iterator Find(const K& val)
{
    //使用仿函数
    KeyOfT kot;
    Node* cur = _root;
    while (cur)
    {
        if (kot(cur->_value) > val)
        {
            //左子树中查找
            cur = cur->_left;
        }
        else if (kot(cur->_value) < val)
        {
            //右子树中查找
            cur = cur->_right;
        }
        else
        {
            //找到了
            return iterator(cur);
        }
    }
    //找不到返回end()
    return end();
}
2.2.2 改造插入

首先我们改造Insert函数,除了比较大于需要用仿函数替换以外,我们还需要对返回值进行改造:返回值是一个pair,第一个参数是一个迭代器,第二个参数是一个bool

  • 若待插入元素的键值key在容器当中不存在,则insert函数插入成功,并返回插入后元素的迭代器和true
  • 若待插入元素的键值key在容器当中已经存在,则insert函数插入失败,并返回容器当中键值为key的元素的迭代器和false
pair<iterator, bool> Insert(const T& data)
{
    //情况一:如果是根节点
    if (_root == nullptr)
    {
        _root = new Node(data);
        _root->_col = BLACK;
        return make_pair(iterator(_root),true);
    }
    KeyOfT kot;
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
        if (kot(cur->_value) < kot(data))
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (kot(cur->_value) > kot(data))
        {
            parent = cur;
            cur = cur->_left;
        }
        else
        {
            return make_pair(iterator(cur), false);
        }
    }
    //找到插入位置
    cur = new Node(data);
    Node* newnode = cur;
    if (kot(parent->_value) < 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;
                parent = cur->_parent;
            }
            else
            {
                //情况四:叔叔不存在/存在且为黑,且cur在parent的左侧
                if (cur == parent->_left)
                {
                    //     g  
                    //   p   u
                    // c 
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else//情况五:叔叔不存在 / 存在且为黑,cur在parent的右侧
                {
                    //     g
                    //   p   u
                    //     c
                    RotateLR(grandfather);
                    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;
                parent = cur->_parent;
            }
            else
            {
                //情况四:叔叔不存在/存在且为黑,且cur在parent的左侧
                if (cur == parent->_right)
                {
                    //    g
                    //  u   p
                    //        c
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else // 情况五:叔叔不存在 / 存在且为黑,cur在parent的右侧
                {
                    //    g
                    //  u   p
                    //    c
                    RotateRL(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                //这时该子树的根节点变为黑色,不需要继续调整
                break;
            }
        }

    }
    //防止情况三改到根节点变为红色
    _root->_col = BLACK;
    return make_pair(iterator(newnode), true);
}
2.2.3 改造删除

改造删除需要注意两点,一点就是我们删除逻辑中复用了查找函数,而查找返回的是是一个迭代器,我们需要的是对应节点的指针,所以我们需要在迭代器中提供一个通过迭代器返回对应指针的函数。

//(一)找到删除节点
iterator it = Find(key);
Node* cur = it.getNode();

第二点就是对于map的键值中的K是被const修饰的无法修改,而在红黑树删除中如果左右孩子都不为空需要采用伪删除法,这时需要对节点的值就性覆盖。为了解决这个问题,我们可以重新创造一个节点对红黑树进行重新链接,再把将原节点删除。

else //待删除结点的左右子树均不为空
{
    //替换法删除
    //寻找待删除结点右子树当中key值最小的结点作为实际删除结点
    Node* minParent = cur;
    Node* minRight = cur->_right;
    while (minRight->_left)
    {
        minParent = minRight;
        minRight = minRight->_left;
    }
    // 原本直接可以赋值
    // cur->_value = minRight->_value 
    //将待删除结点的键值改为minRight的键值
    Node* newnode = new Node(minRight->_value,cur->_col);
    Node* parent = cur->_parent;
    //重新链接祖父孙三代节点关系
    cur->_left->_parent = newnode;
    cur->_right->_parent = newnode;
    if (parent)
    {
        if (parent->_left == cur)
        {
            parent->_left = newnode;
        }
        else
        {
            parent->_right = newnode;
        }
    }
    else
    {
        //如果是根节点
        _root = newnode;
    }
    newnode->_parent = parent;
    newnode->_left = cur->_left;
    newnode->_right = cur->_right;
    //如果minParent是cur
    if (minParent == cur)
    {
        minParent = newnode;
    }
    delete cur;
    delParent = minParent; //标记实际删除的父节点
    delCur = minRight; //标记实际删除的结点
}

3. 迭代器

因为map/set底层为红黑树,节点与节点之间并不连续,所以迭代器也肯定需要进行封装。为了同时支持const迭代器与普通迭代器,我们需要三个模版参数,第一个模版参数T代表数据存储类型,第二个模版参数Ref代表存储类型的引用,第三个模版参数Ptr代表存储类型的指针。然后我们重载如++--等常见操作符。

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
    typedef RBNode<T> Node;
    typedef __RBTreeIterator<T, Ref, Ptr> Self;
    Node* _node;
    //构造
    __RBTreeIterator(Node* node)
        :_node(node)
    {}
    Ref operator*()
    {
        return _node->_value;
    }
    Ptr operator->()
    {
        return &_node->_value;
    }
    //判断两个正向迭代器是否不同
    bool operator!=(const Self& s) const
    {
        return _node != s._node;
    }
    //判断两个正向迭代器是否相同
    bool operator==(const Self& s) const
    {
        return _node == s._node;
    }
    Node* getNode()
    {
        return _node;
    }
};

接下来我们需要在红黑树中编写常见的迭代器函数,因为我们的迭代器是按中序遍历的,所以begin()就是红黑树的最左节点,而end()我们可以先将其设置nullptr
6ca2a542a6344df346352dd0f305613a.png

typedef __RBTreeIterator<T, T&, T*> iterator;//普通迭代器
typedef __RBTreeIterator<T, const T&, const T*> const_iterator;//const迭代器
//最左节点
iterator begin()
{
    Node* cur = _root;
    while (cur && cur->_left)
    {
        cur = cur->_left;
    }
    return iterator(cur);
}
iterator end()
{
    return iterator(nullptr);
}
//const版本begin和end
const_iterator begin()const
{
    Node* cur = _root;
    while (cur && cur->_left)
    {
        cur = cur->_left;
    }
    return const_iterator(cur);
}

const_iterator end()const
{
    return const_iterator(nullptr);
}

接下来我们再重载迭代器的++--。其中++的逻辑为找到中序遍历的下一个节点,其遍历逻辑为:

  • 如果当前结点的右子树不为空,则++操作后应该找到其右子树当中的最左结点。
  • 如果当前结点的右子树为空,则++操作后应该往上找到孩子不在父亲右的祖先。

--的逻辑恰好相反:

  • 如果当前结点的左子树不为空,则--操作后应该找到其左子树当中的最右结点。
  • 如果当前结点的左子树为空,则--操作后应该往上找到孩子不在父亲左的祖先。
 //前置++
 Self& operator++()
 {
     //如果右子树不为空
     if (_node->_right)
     {
         //寻找该结点右子树当中的最左结点
         Node* left = _node->_right;
         while (left->_left)
         {
             left = left->_left;
         }
         _node = left;
     }
     else
     {
         Node* cur = _node;
         Node* parent = cur->_parent;
         //寻找孩子不在右的祖先
         while (parent && cur == parent->_right)
         {
             cur = parent;
             parent = cur->_parent;
         }
         _node = parent;
     }
     return *this;
 }
 //前置--
 Self& operator--()
 {
     if (_node->_left) //结点的左子树不为空
     {
         //寻找该结点左子树当中的最右结点
         Node* right = _node->_left;
         while (right->_right)
         {
             right = right->_right;
         }
         _node = right;
     }
     else
     {
         //寻找孩子不在父亲左的祖先
         Node* cur = _node;
         Node* parent = cur->_parent;
         while (parent && cur == parent->_left)
         {
             cur = parent;
             parent = parent->_parent;
         }
         _node = parent;
     }
     return *this;
 }

然后我们可以在set/map中增加相应的迭代器,其中需要注意的是set中的迭代器为了防止key被修改,将普通迭代器也设为const迭代器。但这就引发了一个问题就是:普通迭代器调用仍是红黑树的普通迭代器,返回普通迭代器并不与const迭代器兼容,为了解决这个问题我们仍需要在迭代器中重载一个普通迭代器初始化const迭代器的构造函数。

//普通迭代器构造const迭代器
__RBTreeIterator(const __RBTreeIterator<T,T&,T*>& it)
    :_node(it._node)
{}

最后我们直接再复用红黑树的接口就可以实现set/map中的成员函数:

template<class K>
class set
{
    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;
    //成员函数
    iterator begin()
    {
        return _t.begin();
    }
    iterator end()
    {
        return _t.end();
    }
    const_iterator begin() const
    {
        return _t.begin();
    }

    const_iterator end() const
    {
        return _t.end();
    }
    pair<iterator, bool> insert(const K& key)
    {
        return _t.Insert(key);
    }
    //删除函数
    void erase(const K& key)
    {
        _t.Erase(key);
    }
    //查找函数
    iterator find(const K& key)
    {
        return _t.Find(key);
    }
private:
    RBTree<K, K, SetKeyOfT> _t;
};
template<class K, class V>
class map
{
	//仿函数
	struct MapKeyOfT
	{
		const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};
public:
	//typename声明是一个类型而不是静态变量
	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();
	}
	pair<iterator, bool> insert(const pair<K,V>& key)
	{
		return _t.Insert(key);
	}
	//删除函数
	void erase(const K& key)
	{
		_t.Erase(key);
	}
	//查找函数
	iterator find(const K& key)
	{
		return _t.Find(key);
	}
	//[]运算符重载
	V& operator[](const K& key)
	{
		pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
		return ret.first->second;
	}
private:
	RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

其实我们设计的迭代器是有一个问题的,那就是end()如果进行--操作按理来说应该是最后一个节点,但是我们设置为nullptr就无法实现这一点。而在STL库中是这样解决这个问题的:
4960816bc62a53917de6c691ee7526a5 (1).png
对根节点增加一个头节点,让这个头节点的左指针指向最左节点,右指针指向最右节点。然后让用头节点的左节点构造begin(),头节点本身构造end(),这样就能实现--之后指向最右节点。但这样一来我们所有的逻辑都需要改变,而我们原本的逻辑除了这点无法满足外,并没有太大影响,所以最后就不在实现这种版本了。

思考题:为什么需要第一个模版参数K,不是可以通过第二个模版参数获取K吗?

对于set来说,直接使用第二个模版参数时没有任何问题的,但是对于map来说,查找与删除等操作是以key作为查找基准的,虽然说可以通过.first获取对应的key元素,但是由于setmap封装的是同一颗红黑树,set中的key并不存在像.first这样的操作。所以为了使setmap能封装同一棵红黑树,最好增加一个模版参数获取对应的key

4. 源码

4.1 RBTree.h

#pragma once
#include<utility>
enum _col
{
    RED,//红色
    BLACK//黑色
};
template<class T>
struct RBNode
{
    RBNode(const T& value = T(), _col col = RED)
        :_left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _value(value)
        , _col(col)//默认为红节点
    {}
    RBNode<T>* _left;//左子树
    RBNode<T>* _right;//右子树
    RBNode<T>* _parent;//父节点
    T _value;//键值
    _col _col;//颜色
};
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
    typedef RBNode<T> Node;
    typedef __RBTreeIterator<T, Ref, Ptr> Self;
    Node* _node;
    //构造
    __RBTreeIterator(Node* node)
        :_node(node)
    {}
    //普通迭代器构造const迭代器
    __RBTreeIterator(const __RBTreeIterator<T,T&,T*>& it)
        :_node(it._node)
    {}
    Ref operator*()
    {
        return _node->_value;
    }
    Ptr operator->()
    {
        return &_node->_value;
    }
    //判断两个正向迭代器是否不同
    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* left = _node->_right;
            while (left->_left)
            {
                left = left->_left;
            }
            _node = left;
        }
        else
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            //寻找孩子不在右的祖先
            while (parent && cur == parent->_right)
            {
                cur = parent;
                parent = cur->_parent;
            }
            _node = parent;
        }
        return *this;
    }
    //前置--
    Self& operator--()
    {
        if (_node->_left) //结点的左子树不为空
        {
            //寻找该结点左子树当中的最右结点
            Node* right = _node->_left;
            while (right->_right)
            {
                right = right->_right;
            }
            _node = right;
        }
        else
        {
            //寻找孩子不在父亲左的祖先
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_left)
            {
                cur = parent;
                parent = parent->_parent;
            }
            _node = parent;
        }
        return *this;
    }
    Node* getNode()
    {
        return _node;
    }
};
template<class K, class T, class KeyOfT>
class RBTree
{
    typedef RBNode<T> Node;
public:
    typedef __RBTreeIterator<T, T&, T*> iterator;//普通迭代器
    typedef __RBTreeIterator<T, const T&, const T*> const_iterator;//const迭代器
    //最左节点
    iterator begin()
    {
        Node* cur = _root;
        while (cur && cur->_left)
        {
            cur = cur->_left;
        }
        return iterator(cur);
    }
    iterator end()
    {
        return iterator(nullptr);
    }
    //const版本begin和end
    const_iterator begin()const
    {
        Node* cur = _root;
        while (cur && cur->_left)
        {
            cur = cur->_left;
        }
        return const_iterator(cur);
    }

    const_iterator end()const
    {
        return const_iterator(nullptr);
    }
    RBTree()
    {}
    RBTree(const RBTree<K, T, KeyOfT>& t)
    {
        _root = copy(t._root);
    }
    RBTree<K, T, KeyOfT> operator =(const RBTree<K, T, KeyOfT> t)
    {
        this->swap(_root, t._root);
        return *this;
    }
    pair<iterator, bool> Insert(const T& data)
    {
        //情况一:如果是根节点
        if (_root == nullptr)
        {
            _root = new Node(data);
            _root->_col = BLACK;
            return make_pair(iterator(_root),true);
        }
        KeyOfT kot;
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            if (kot(cur->_value) < kot(data))
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (kot(cur->_value) > kot(data))
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return make_pair(iterator(cur), false);
            }
        }
        //找到插入位置
        cur = new Node(data);
        Node* newnode = cur;
        if (kot(parent->_value) < 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;
                    parent = cur->_parent;
                }
                else
                {
                    //情况四:叔叔不存在/存在且为黑,且cur在parent的左侧
                    if (cur == parent->_left)
                    {
                        //     g  
                        //   p   u
                        // c 
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else//情况五:叔叔不存在 / 存在且为黑,cur在parent的右侧
                    {
                        //     g
                        //   p   u
                        //     c
                        RotateLR(grandfather);
                        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;
                    parent = cur->_parent;
                }
                else
                {
                    //情况四:叔叔不存在/存在且为黑,且cur在parent的左侧
                    if (cur == parent->_right)
                    {
                        //    g
                        //  u   p
                        //        c
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else // 情况五:叔叔不存在 / 存在且为黑,cur在parent的右侧
                    {
                        //    g
                        //  u   p
                        //    c
                        RotateRL(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    //这时该子树的根节点变为黑色,不需要继续调整
                    break;
                }
            }

        }
        //防止情况三改到根节点变为红色
        _root->_col = BLACK;
        return make_pair(iterator(newnode), true);
    }
    void RotateR(Node* parent)
    {
        Node* cur = parent;
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        parent->_left = subLR;
        if (subLR)
            subLR->_parent = parent;
        subL->_right = parent;
        Node* ppNode = parent->_parent;
        parent->_parent = subL;
        if (parent == _root)
        {
            _root = subL;
            _root->_parent = nullptr;
        }
        else
        {
            if (ppNode->_left == parent)
            {
                ppNode->_left = subL;
            }
            else
            {
                ppNode->_right = subL;
            }
            subL->_parent = ppNode;
        }
    }
    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        parent->_right = subRL;
        if (subRL)
            subRL->_parent = parent;

        subR->_left = parent;
        Node* ppNode = parent->_parent;

        parent->_parent = subR;

        if (parent == _root)
        {
            _root = subR;
            _root->_parent = nullptr;
        }
        else
        {
            if (ppNode->_right == parent)
            {
                ppNode->_right = subR;
            }
            else
            {
                ppNode->_left = subR;
            }
            subR->_parent = ppNode;
        }
    }
    void RotateLR(Node* parent)
    {
        RotateL(parent->_left);
        RotateR(parent);
    }
    void RotateRL(Node* parent)
    {
        RotateR(parent->_right);
        RotateL(parent);
    }
    iterator Find(const K& val)
    {
        //使用仿函数
        KeyOfT kot;
        Node* cur = _root;
        while (cur)
        {
            if (kot(cur->_value) > val)
            {
                //左子树中查找
                cur = cur->_left;
            }
            else if (kot(cur->_value) < val)
            {
                //右子树中查找
                cur = cur->_right;
            }
            else
            {
                //找到了
                return iterator(cur);
            }
        }
        //找不到返回end()
        return end();
    }

    //删除函数
    bool Erase(const K& key)
    {
        //(一)找到删除节点
        iterator it = Find(key);
        Node* cur = it.getNode();
        //未找到返回false
        if (cur == nullptr)
        {
            return false;
        }
        //记录父节点
        Node* parent = cur->_parent;
        //用于标记实际的待删除结点及其父结点
        Node* delParent = nullptr;
        Node* delCur = nullptr;
        if (cur->_left == nullptr) //待删除结点的左子树为空
        {
            if (cur == _root) //待删除结点是根结点
            {
                _root = _root->_right; //让根结点的右子树作为新的根结点
                if (_root)
                {
                    _root->_parent = nullptr;
                    _root->_col = BLACK; //根结点为黑色
                }
                delete cur; //删除原根结点
                return true;
            }
            else
            {
                delParent = parent; //标记实际删除结点的父结点
                delCur = cur; //标记实际删除的结点
            }
        }
        else if (cur->_right == nullptr) //待删除结点的右子树为空
        {
            if (cur == _root) //待删除结点是根结点
            {
                _root = _root->_left; //让根结点的左子树作为新的根结点
                if (_root)
                {
                    _root->_parent = nullptr;
                    _root->_col = BLACK; //根结点为黑色
                }
                delete cur; //删除原根结点
                return true;
            }
            else
            {
                delParent = parent; //标记实际删除结点的父结点
                delCur = cur; //标记实际删除的结点
            }
        }
        else //待删除结点的左右子树均不为空
        {
            //替换法删除
            //寻找待删除结点右子树当中key值最小的结点作为实际删除结点
            Node* minParent = cur;
            Node* minRight = cur->_right;
            while (minRight->_left)
            {
                minParent = minRight;
                minRight = minRight->_left;
            }
            // 原本直接可以赋值
            // cur->_value = minRight->_value 
            //将待删除结点的键值改为minRight的键值
            Node* newnode = new Node(minRight->_value,cur->_col);
            Node* parent = cur->_parent;
            //重新链接祖父孙三代节点关系
            cur->_left->_parent = newnode;
            cur->_right->_parent = newnode;
            if (parent)
            {
                if (parent->_left == cur)
                {
                    parent->_left = newnode;
                }
                else
                {
                    parent->_right = newnode;
                }
            }
            else
            {
                //如果是根节点
                _root = newnode;
            }
            newnode->_parent = parent;
            newnode->_left = cur->_left;
            newnode->_right = cur->_right;
            //如果minParent是cur
            if (minParent == cur)
            {
                minParent = newnode;
            }
            delete cur;
            delParent = minParent; //标记实际删除的父节点
            delCur = minRight; //标记实际删除的结点
        }
        //记录待删除结点及其父结点,便于后面删除
        Node* del = delCur;
        Node* delP = delParent;
        //(二)调整红黑树
        AdjustRBTree(delCur, delParent);
        //(三)进行实际删除
        DeleteNode(del, delP);
        return true;
    }
    void DeleteNode(Node* del, Node* delP)
    {
        if (del->_left == nullptr) //实际删除结点的左子树为空
        {
            if (del == delP->_left) //实际删除结点是其父结点的左孩子
            {
                delP->_left = del->_right;
                //指向父节点
                if (del->_right)
                    del->_right->_parent = delP;
            }
            else //实际删除结点是其父结点的右孩子
            {
                delP->_right = del->_right;
                if (del->_right)
                    del->_right->_parent = delP;
            }
        }
        else //实际删除结点的右子树为空
        {
            if (del == delP->_left) //实际删除结点是其父结点的左孩子
            {
                delP->_left = del->_left;
                if (del->_left)
                    del->_left->_parent = delP;
            }
            else //实际删除结点是其父结点的右孩子
            {
                delP->_right = del->_left;
                if (del->_left)
                    del->_left->_parent = delP;
            }
        }
        delete del; //实际删除结点
    }
    void AdjustRBTree(Node* delCur, Node* delParent)
    {
        if (delCur->_col == BLACK) //删除的是黑色结点
        {
            if (delCur->_left) //待删除结点有一个红色的左孩子(不可能是黑色)
            {
                delCur->_left->_col = BLACK; //将这个红色的左孩子变黑即可
            }
            else if (delCur->_right) //待删除结点有一个红色的右孩子(不可能是黑色)
            {
                delCur->_right->_col = BLACK; //将这个红色的右孩子变黑即可
            }
            else //待删除结点的左右均为空
            {
                while (delCur != _root) //可能一直调整到根结点
                {
                    if (delCur == delParent->_left) //待删除结点是其父结点的左孩子
                    {
                        Node* brother = delParent->_right; //兄弟结点是其父结点的右孩子
                        //情况一:brother为红色
                        if (brother->_col == RED)
                        {
                            delParent->_col = RED;
                            brother->_col = BLACK;
                            RotateL(delParent);
                            //需要继续处理
                            brother = delParent->_right; //更新brother
                        }
                        //情况二:brother为黑色,且其左右孩子都是黑色结点或为空
                        if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
                            && ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
                        {
                            brother->_col = RED;
                            if (delParent->_col == RED)
                            {
                                delParent->_col = BLACK;
                                break;
                            }
                            //需要继续处理
                            delCur = delParent;
                            delParent = delCur->_parent;
                        }
                        else
                        {
                            //情况三:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空
                            if ((brother->_right == nullptr) || (brother->_right->_col == BLACK))
                            {
                                brother->_left->_col = BLACK;
                                brother->_col = RED;
                                RotateR(brother);
                                //需要继续处理
                                brother = delParent->_right; //更新brother
                            }
                            //情况四:brother为黑色,且其右孩子是红色结点
                            brother->_col = delParent->_col;
                            delParent->_col = BLACK;
                            brother->_right->_col = BLACK;
                            RotateL(delParent);
                            break; //情况四执行完毕后调整一定结束
                        }
                    }
                    else //delCur == delParent->_right //待删除结点是其父结点的右孩子
                    {
                        Node* brother = delParent->_left; //兄弟结点是其父结点的左孩子
                        //情况一:brother为红色
                        if (brother->_col == RED) //brother为红色
                        {
                            delParent->_col = RED;
                            brother->_col = BLACK;
                            RotateR(delParent);
                            //需要继续处理
                            brother = delParent->_left; //更新brother
                        }
                        //情况二:brother为黑色,且其左右孩子都是黑色结点或为空
                        if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
                            && ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
                        {
                            brother->_col = RED;
                            if (delParent->_col == RED)
                            {
                                delParent->_col = BLACK;
                                break;
                            }
                            //需要继续处理
                            delCur = delParent;
                            delParent = delCur->_parent;
                        }
                        else
                        {
                            //情况三:brother为黑色,且其右孩子是红色结点,左孩子是黑色结点或为空
                            if ((brother->_left == nullptr) || (brother->_left->_col == BLACK))
                            {
                                brother->_right->_col = BLACK;
                                brother->_col = RED;
                                RotateL(brother);
                                //需要继续处理
                                brother = delParent->_left; //更新brother
                            }
                            //情况四:brother为黑色,且其左孩子是红色结点
                            brother->_col = delParent->_col;
                            delParent->_col = BLACK;
                            brother->_left->_col = BLACK;
                            RotateR(delParent);
                            break; //情况四执行完毕后调整一定结束
                        }
                    }
                }
            }
        }
    }
    ~RBTree()
    {
        Destroy(_root);
    }
private:
    void Destroy(Node*& root)
    {
        if (root == nullptr)
        {
            return;
        }
        //递归销毁左子树
        Destroy(root->_left);
        //递归销毁右子树
        Destroy(root->_right);
        //销毁根节点
        delete root;
        root = nullptr;
    }


    Node* copy(Node* root)
    {
        // 如果原始节点为空,直接返回空指针
        if (root == nullptr)
        {
            return nullptr;
        }
        // 为新节点分配内存并拷贝原始节点的值
        Node* newnode = new Node(root->_kv);
        // 递归拷贝左子树
        newnode->_left = copy(root->_left);
        // 递归拷贝右子树
        newnode->_right = copy(root->_right);
        // 将新节点的父节点指针置为空
        newnode->_parent = nullptr;
        // 拷贝原始节点的颜色信息
        newnode->_col = root->_col;
        // 如果新节点的左子节点存在,设置其父节点为新节点
        if (newnode->_left)
        {
            newnode->_left->_parent = newnode;
        }
        // 如果新节点的右子节点存在,设置其父节点为新节点
        if (newnode->_right)
        {
            newnode->_right->_parent = newnode;
        }
    }

    Node* _root = nullptr;
};

4.2 set.h

#pragma once
#include"RBTree.h"
namespace betty
{
	template<class K>
	class set
	{
		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;
		//成员函数
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		const_iterator begin() const
		{
			return _t.begin();
		}

		const_iterator end() const
		{
			return _t.end();
		}
		pair<iterator, bool> insert(const K& key)
		{
			return _t.Insert(key);
		}
		//删除函数
		void erase(const K& key)
		{
			_t.Erase(key);
		}
		//查找函数
		iterator find(const K& key)
		{
			return _t.Find(key);
		}
	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
}

4.3 map.c

#pragma once
#include"RBTree.h"
namespace betty
{
	template<class K, class V>
	class map
	{
		//仿函数
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		//typename声明是一个类型而不是静态变量
		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();
		}
		pair<iterator, bool> insert(const pair<K,V>& key)
		{
			return _t.Insert(key);
		}
		//删除函数
		void erase(const K& key)
		{
			_t.Erase(key);
		}
		//查找函数
		iterator find(const K& key)
		{
			return _t.Find(key);
		}
		//[]运算符重载
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};
}

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

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

相关文章

如何在Spring中为`@Value`注解设置默认值

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

如何处理在学校Linux连接不上服务器

一、问题描述 当我们在周末在图书馆背着室友偷偷学习时&#xff0c;准备好好学习Linux&#xff0c;争取在日后大展拳脚时&#xff0c;却突然尴尬的发现&#xff0c;连接不上服务器&#xff0c;总是出现以下画面&#xff1a; 那么&#xff0c;我们该如何解决问题呢&#xff1f; …

螺杆支撑座与滚珠丝杆的精准适配!

螺杆支撑座与滚珠丝杆的适配是确保机械系统的稳定性、精度和耐用性的关键&#xff0c;其适配方法主要包括螺纹连接、联轴器连接、锁紧连接。 螺杆支撑座种类多样&#xff0c;每种类型都有其特定的适用范围和性能特点。因此&#xff0c;根据滚珠丝杆的规格和应用需求&#xff0c…

Python接口测试之如何使用requests发起请求例子解析

在Python中&#xff0c;使用requests库发起HTTP请求是一种常见的接口测试方法。以下是一些使用requests库的基本示例&#xff0c;涵盖了GET、POST、PUT、DELETE等HTTP方法。 安装requests库 首先&#xff0c;确保你已经安装了requests库。如果未安装&#xff0c;可以通过以下…

【系统分析师】-案例篇-数据库

1、分布式数据库 1&#xff09;请用300字以内的文字简述分布式数据库跟集中式数据库相比的优点。 &#xff08;1&#xff09;坚固性好。由于分布式数据库系统在个别结点或个别通信链路发生故障的情况下&#xff0c;它仍然可以降低级别继续工作&#xff0c;系统的坚固性好&…

线程:线程创建pthread_create,线程间的同步与互斥

线程的创建 线程的创建是通过调用pthread_create函数来实现的。该函数的原型如下&#xff1a; int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);参数说明&#xff1a; thread&#xff1a;指向pthread_t类型…

开源word文档相似度对比 软件WinMerge

WinMerge 官网下载 &#xff1a;GitHub - WinMerge/winmerge: WinMerge is an Open Source differencing and merging tool for Windows. WinMerge can compare both folders and files, presenting differences in a visual text format that is easy to understand and hand…

ros2_python编程_多个文件python打包_目录拷贝_解决import错误问题ModuleNotFoundError

1.问题 ros2 python编写程序, 有多个python文件 如何打包多个python文件?解决import错误问题如何打包 有python目录结构的工程 1.ros2 多个python文件示例 代码目录结构, gitee 在线代码 tree 7_multi_file_setup/ 7_multi_file_setup/ ├── file1.py ├── main_node.…

飞书怎么关联任意两段话

最近开始用飞书记文档&#xff0c;体验实在是非常的丝滑&#xff0c;对我来说感觉没有找到更好的竞品了。废话不多说&#xff0c;接下来简单介绍一下怎么关联任意两段话吧。 首先说明&#xff0c;关联可以单向&#xff0c;也可以双向。 直接举例。 我想要将蓝字关联到最下面的…

国标GB28181视频监控EasyCVR视频汇聚平台国标注册被陌生IP入侵如何处理?

GB28181国标/GA/T1400协议/安防综合管理系统EasyCVR视频汇聚平台能在复杂的网络环境中&#xff0c;将前端设备统一集中接入与汇聚管理。智慧安防/视频存储/视频监控/视频汇聚EasyCVR平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级…

Java基础(包装类)

文章目录 前言 一、包装类的概述 二、自动拆装箱 三、128陷阱&#xff08;面试重点&#xff09; 四、自动拆装箱例题分析 前言 该篇文章创作时参考查阅了如下文章 Java种的包装类 Java包装类&#xff08;自动拆装箱&#xff09; Java--自动拆箱/装箱/实例化顺序/缓存…

第三期书生大模型实战营之茴香豆工具实践

文章目录 基础任务作业记录1. 环境准备2. 模型准备3. 修改配置文件4. 知识库创建6. 启动茴香豆webui 基础任务 在 InternStudio 中利用 Internlm2-7b 搭建标准版茴香豆知识助手&#xff0c;并使用 Gradio 界面完成 2 轮问答&#xff08;问题不可与教程重复&#xff0c;作业截图…

IDEA2023版本创建SSM项目框架

按图中红色数字顺序&#xff0c;先点击Maven&#xff0c;设置该项目为maven构建管理的项目&#xff0c;然后点击create进行项目创建 配置该项目的相关maven信息&#xff0c;按下图顺序进入到maven配置页面后进行本地maven相关信息配置。 创建web模块依次按下图中顺序进行点击 配…

朴世龙院士团队《Global Change Biology 》精确量化全球植被生产力对极端温度的响应阈值!

本文首发于“生态学者”微信公众号&#xff01; 随着全球气候变暖的加剧&#xff0c;极端温度事件对陆地生态系统的影响日益显著。植被作为生态系统的重要组成部分&#xff0c;其生产力对温度变化的响应尤为敏感。然而&#xff0c;关于极端温度如何以及在何种程度上影响植被生产…

TCP三次握手过程详解

三次握手过程&#xff1a; 客户端视角&#xff1a; 1.客户端调用connect&#xff0c;开启计时器&#xff0c;发送SYN包&#xff0c;如果重传超时&#xff0c;认为连接失败 2.如果收到服务端的ACK&#xff0c;则进入ESTABLISHED状态 3.清除重传计时器&#xff0c;发送ACK&…

windows权限维持汇总

Windows 权限维持 一、文件层面 1&#xff09;attrib 使用 Attrib s a h r 命令 s&#xff1a;设置系统属性&#xff08;System&#xff09; a&#xff1a;设置存档属性&#xff08;Archive&#xff09; h&#xff1a;设置隐藏属性&#xff08;Hidden&#xff09; r&#…

深度学习基础--11个专题带你入门Pytorch上

目的 本专栏更新深度学习基础&#xff0c;包括pytorch、tensorflow的使用以及CNN、RNN、yolo、GAN、LSTM、Transformer等神经网络的理论基础 前言 Pytorch是最常用的深度学习框架&#xff0c;里面包含了大量关于构建神经网络及其运算的APIPytorch基础入门分为上下两篇文章&am…

基于事件总线EventBus实现邮件推送功能

什么是事件总线 事件总线是对发布-订阅模式的一种实现。它是一种集中式事件处理机制&#xff0c;允许不同的组件之间进行彼此通信而又不需要相互依赖&#xff0c;达到一种解耦的目的。 关于这个概念&#xff0c;网上有很多讲解的&#xff0c;这里我推荐一个讲的比较好的&#x…

光伏设计时要画出哪些模型?

在光伏系统的设计中&#xff0c;为了确保项目的顺利实施与高效运行&#xff0c;设计师需要绘制多种模型来综合考虑各种因素&#xff0c;包括参照物、障碍物以及楼顶配房等。这些模型不仅有助于预测光伏系统的发电效率&#xff0c;还能帮助规划最佳的安装布局&#xff0c;减少阴…

碎片笔记|Computer Journal 期刊投稿注意事项

前言&#xff1a;3月份把之前做的一篇工作转投到了computer journal&#xff0c;8月7号来信说我投稿的工作之前因为挂在arXiv上&#xff0c;因此和正常的投稿要求不太一致&#xff0c;需要更换投稿方式&#xff0c;编辑提供了两种选择如下。 The first choice is to keep your …