【c++丨STL】基于红黑树模拟实现set和map(附源码)

简介: 本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。

前言

之前我们学习了红黑树以及STL中的set和map两种容器,本篇文章,基于之前实现的红黑树代码,我们将仿照SGI STL的实现方式,尝试对同一棵红黑树进行封装和一系列适配修改,模拟实现set和map两种容器。

建议大家掌握了红黑树以及set和map的使用之后,再来阅读本文,否则部分内容可能较难理解。

一、红黑树源码

之前实现的红黑树源代码如下:

#include <iostream>
#include <utility>
using namespace std;

enum Color
{
   
    RED,
    BLACK
};

template<class K, class V>
struct RBTreeNode
{
   
    pair<K, V> _kv;
    RBTreeNode<K, V>* _left;
    RBTreeNode<K, V>* _right;
    RBTreeNode<K, V>* _parent;
    Color _col;
    RBTreeNode(const pair<K, V>& kv)
        :_kv(kv)
        , _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _col(RED)
    {
   }
};

template<class K, class V>
class RBTree
{
   
    typedef RBTreeNode<K, V> Node;
public:
    RBTree() = default;
    RBTree(const RBTree<K, V>& t)
    {
   
        _root = _Copy(t._root);
    }
    ~RBTree()
    {
   
        _Destroy(_root);
    }
    bool Insert(const pair<K, V>& kv)
    {
   
        if (_root == nullptr)
        {
   
            _root = new Node(kv);
            _root->_col = BLACK;
            return true;
        }
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
   
            if (kv.first < cur->_kv.first)
            {
   
                parent = cur;
                cur = cur->_left;
            }
            else if (kv.first > cur->_kv.first)
            {
   
                parent = cur;
                cur = cur->_right;
            }
            else
            {
   
                return false;
            }
        }
        cur = new Node(kv);
        if (kv.first < parent->_kv.first)
        {
   
            parent->_left = cur;
        }
        else
        {
   
            parent->_right = 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
                {
   
                    if (cur == parent->_left)
                    {
   
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
   
                        RotateL(parent);
                        RotateR(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
                {
   
                    if (cur == parent->_right)
                    {
   
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
   
                        RotateR(parent);
                        RotateL(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
        }
        _root->_col = BLACK;
        return true;
    }
    Node* Find(const K& key)
    {
   
        Node* cur = _root;
        while (cur)
        {
   
            if (key < cur->_kv.first)
            {
   
                cur = cur->_left;
            }
            else if (key > cur->_kv.first)
            {
   
                cur = cur->_right;
            }
            else
            {
   
                return cur;
            }
        }
        return nullptr;
    }
private:
    void RotateR(Node* parent)
    {
   
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        parent->_left = subLR;
        if (subLR) subLR->_parent = parent;
        Node* ppNode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;
        if (parent == _root)
        {
   
            _root = subL;
            subL->_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;
        Node* ppNode = parent->_parent;
        subR->_left = parent;
        parent->_parent = subR;
        if (parent == _root)
        {
   
            _root = subR;
            subR->_parent = nullptr;
        }
        else
        {
   
            if (ppNode->_left == parent) ppNode->_left = subR;
            else ppNode->_right = subR;
            subR->_parent = ppNode;
        }
    }
    Node* _Copy(Node* root, Node* parent = nullptr)
    {
   
        if (root == nullptr) return nullptr;
        Node* NewRoot = new Node(root->_kv);
        NewRoot->_col = root->_col;
        NewRoot->_parent = parent;
        NewRoot->_left = _Copy(root->_left, NewRoot);
        NewRoot->_right = _Copy(root->_right, NewRoot);
        return NewRoot;
    }
    void _Destroy(Node* root)
    {
   
        if (root == nullptr) return;
        _Destroy(root->_left);
        _Destroy(root->_right);
        delete root;
    }
    Node* _root = nullptr;
};

二、set和map的模拟实现

1. 数据类型的设置

由于set元素类型是键(key),而map键值对(key_value),它们底层的数据类型是不同的,那么如何通过封装同一棵红黑树实现两种不同数据类型的容器呢?解决方法:通过传入不同的模板参数类型决定红黑树的数据类型

template<class K>
class Set
{
   
public:
private:
    RBTree<K, const K> _rbtree;
};
template<class K, class V>
class Map
{
   
public:
private:
    RBTree<K, pair<const K, V>> _rbtree;
};

我们将红黑树作为set和map的成员,在红黑树的第二个模板参数处,set传入const K(加const保证K不被修改);map传入pair,这样红黑树就能根据不同的类型实例化出不同对象,以适应set和map的不同需求。

相应地,我们需要对红黑树节点结构红黑树的模板参数名进行修改:

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

如此,当我们创建set对象时,传入模板参数const K,底层红黑树接收到的参数T就是K(键),节点存放的数据类型就是K;创建map对象时,传入模板参数pair,红黑树接收到的参数T就是(键值对),节点存放的数据类型就是。

image.png

既然第二个模板参数就可以决定红黑树的数据类型,那么是否可以取消第一个模板参数呢?

其实不然。 虽然set和map的数据元素类型不同,但查找或删除操作都是按键查找/删除,函数的参数必须传入K类型。如果只是实现set容器,那么第二个参数的作用就完全可以替代第一个参数;但对于map容器,则K类型的第一个模板参数无法省去。所以第一个模板参数存在的意义是为了保证查找和删除操作的参数正确性

2. 键值比较的适配

我们都知道,set和map是关联式容器,在进行插入或查找操作时,难免要和其他元素进行比较。虽然两者的数据类型不同,但比较的对象都是键Key。

但之前我们已经对红黑树节点的数据类型进行了修改:set为K,map为。怎么使它们在进行元素比较时都按照Key值进行比较呢?解决方法:针对不同容器实现不同仿函数,对数据类型进行适配转化。

对于set,数据类型就是K,返回原值即可:

struct SetKeyOfT
{
   
    const K& operator()(const K& key)
    {
   
        return key;
    }
};

而对于map而言,获取到的数据类型是pair,需要返回其第一个成员K,用作比较。

struct MapKeyOfT
{
   
    const K& operator()(const pair<const K, V>& _kv)
    {
   
        return _kv.first;
    }
};

接下来,我们需要给红黑树新增一个模板参数KeyOfT,用于接收两种不同的仿函数:

template<class K, class T, class KeyOfT>
class RBTree

在set和map中传入各自的仿函数给第三个模板参数:

RBTree<K, const K, SetKeyOfT> _rbtree;
RBTree<K, pair<const K, V>, MapKeyOfT> _rbtree;

最后,修改红黑树的InsertFind代码, 以适配Key值比较:

(注:所有需要进行键值比较的位置都需要进行仿函数转化。)

KeyOfT _kot;//红黑树中新增一个仿函数对象便于调用
bool Insert(const T& data)
{
   
    if (_root == nullptr)
    {
   
        _root = new Node(data);
        _root->_col = BLACK;
        return true;
    }
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
   
        if(_kot(data) < _kot(cur->_data))
        {
   
            parent = cur;
            cur = cur->_left;
        }
        else if(_kot(data) > _kot(cur->_data))
        {
   
            parent = cur;
            cur = cur->_right;
        }
        else
        {
   
            return false;
        }
    }
    cur = new Node(data);
    if (_kot(data) < _kot(parent->_data))
    {
   
        parent->_left = cur;
    }
    else
    {
   
        parent->_right = 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
            {
   
                if (cur == parent->_left)
                {
   
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else
                {
   
                    RotateL(parent);
                    RotateR(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
            {
   
                if (cur == parent->_right)
                {
   
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else
                {
   
                    RotateR(parent);
                    RotateL(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                break;
            }
        }
    }
    _root->_col = BLACK;
    return true;
}
Node* Find(const K& key)
{
   
    Node* cur = _root;
    while (cur)
    {
   
        if(key < _kot(cur->_data))
        {
   
            cur = cur->_left;
        }
        else if(key > _kot(cur->_data))
        {
   
            cur = cur->_right;
        }
        else
        {
   
            return cur;
        }
    }
    return nullptr;
}

这样,尽管数据类型不同,但set和map传入各自的仿函数,然后对所有需要进行比较的data值进行适当的转化,就能保证插入和查找时进行比较的是Key值。

image.png

3. 迭代器实现

set和map的迭代器是双向迭代器,由于红黑树特殊的物理结构,我们实现迭代器的方式与list相同,也是对原生指针进行封装,然后通过重载运算符实现指针的各种行为

为减少代码冗余,本次的迭代器实现当中,两种容器也将封装同一份迭代器代码。

迭代器类型定义如下:

template<class T, class Ref, class Ptr>
struct rbtree_iterator
{
   
    typedef RBTreeNode<T> Node;
    typedef rbtree_iterator<T, Ref, Ptr> Self;
    Node* _ptr;
    Node* _root;
};

接下来实现迭代器的各种操作接口:

迭代器的构造函数

rbtree_iterator(Node* ptr, Node* root)
    :_ptr(ptr)
    ,_root(root)
{
   }

*重载和->重载

这两种运算符重载的实现方式与list相同,返回_ptr指向data的值以及地址。

Ref operator*()
{
   
    return _ptr->_data;
}
Ptr operator->()
{
   
    return &(_ptr->_data);
}

前置++

由于set和map的正向遍历遵循二叉树的中序遍历原则,因此实现operator++时,需要找到当前节点在中序遍历中的下一个节点,然后移动指针。有两种情况需要讨论:

情况一:当前节点的右子树不为空

image.png

如图,当前节点是2时,其右子树不为空,中序需要访问的下一个节点就是3;当前节点是4时,中序的下一个节点是5;当前节点是6,中序的下一个节点就是7。所以当前节点的右子树不为空时,寻找右子树的最小值(也就是右子树的最左节点)。

情况二:当前节点的右子树为空

image.png

由图可知,当前节点的右子树为空时,下一个节点不外乎在其祖先当中。所以要让cur沿着祖先路径向上寻找。寻找过程中,若cur是其父亲的左孩子,则父亲就是下一个访问的节点;若cur已经走到根节点处,则说明已经遍历完成

如上图所示

当前节点是3,右子树为空,则向上寻找。3是2的右孩子,继续向上寻找。2是4的左孩子,则4是中序的下一个节点。

当前节点是9,右子树为空,向上寻找。直到找到根节点4,也没有出现左孩子,说明遍历完成。

代码实现:

Self& operator++()
{
   
    if (_ptr->_right)
    {
   
        Node* rightMin = _ptr->_right;
        while (rightMin->_left)
        {
   
            rightMin = rightMin->_left;
        }
        _ptr = rightMin;
    }
    else
    {
   
        Node* cur = _ptr;
        Node* parent = cur->_parent;
        while (parent && parent->_right == cur)
        {
   
            cur = parent;
            parent = parent->_parent;
        }
        _ptr = parent;
    }
    return *this;
}

前置--

前置--的实现逻辑与前置++基本相似(遵循 “ 反向中序遍历 ” ),但需要考虑已经遍历结束(_ptr指向空)的情况:

当_ptr指向空时,--操作需要找到前一个节点(也就是中序遍历的最后一个节点),即整颗树的最右节点。

其余实现逻辑与++操作刚好对称:

若当前节点左子树不为空,则寻找左子树最右节点。

若左子树为空,则向上寻找,直到cur是父亲的右孩子或走到根节点处。

代码实现:

Self& operator--()
{
   
    if (_ptr == nullptr)
    {
   
        Node* cur = _root;
        while (cur && cur->_right)
        {
   
            cur = cur->_right;
        }
        _ptr = cur;
    }
    else if (_ptr->_left)
    {
   
        Node* leftMax = _ptr->_left;
        while (leftMax->_right)
        {
   
            leftMax = leftMax->_right;
        }
        _ptr = leftMax;
    }
    else
    {
   
        Node* cur = _ptr;
        Node* parent = cur->_parent;
        while(parent && parent->_left == cur)
        {
   
            cur = parent;
            parent = parent->_parent;
        }
        _ptr = parent;
    }
    return *this;
}

后置++和--

后置++/--直接调用前置++/--,然后记录中间变量即可。

Self operator++(int)
{
   
    Self tmp(*this);
    ++(*this);
    return tmp;
}
Self operator--(int)
{
   
    Self tmp(*this);
    --(*this);
    return tmp;
}

operator!=

判断它们的_ptr指针是否相等。

bool operator!=(const Self& it)
{
   
    return _ptr != it._ptr;
}

迭代器全部实现代码:

template<class T, class Ref, class Ptr>
struct rbtree_iterator
{
   
    typedef RBTreeNode<T> Node;
    typedef rbtree_iterator<T, Ref, Ptr> Self;
    Node* _ptr;
    Node* _root;
    rbtree_iterator(Node* ptr, Node* root)
        :_ptr(ptr)
        ,_root(root)
    {
   }
    Ref operator*()
    {
   
        return _ptr->_data;
    }
    Ptr operator->()
    {
   
        return &(_ptr->_data);
    }
    Self& operator++()
    {
   
        if (_ptr->_right)
        {
   
            Node* rightMin = _ptr->_right;
            while (rightMin->_left)
            {
   
                rightMin = rightMin->_left;
            }
            _ptr = rightMin;
        }
        else
        {
   
            Node* cur = _ptr;
            Node* parent = cur->_parent;
            while (parent && parent->_right == cur)
            {
   
                cur = parent;
                parent = parent->_parent;
            }
            _ptr = parent;
        }
        return *this;
    }
    Self& operator--()
    {
   
        if (_ptr == nullptr)
        {
   
            Node* cur = _root;
            while (cur && cur->_right)
            {
   
                cur = cur->_right;
            }
            _ptr = cur;
        }
        else if (_ptr->_left)
        {
   
            Node* leftMax = _ptr->_left;
            while (leftMax->_right)
            {
   
                leftMax = leftMax->_right;
            }
            _ptr = leftMax;
        }
        else
        {
   
            Node* cur = _ptr;
            Node* parent = cur->_parent;
            while(parent && parent->_left == cur)
            {
   
                cur = parent;
                parent = parent->_parent;
            }
            _ptr = parent;
        }
        return *this;
    }
    Self operator++(int)
    {
   
        Self tmp(*this);
        ++(*this);
        return tmp;
    }
    Self operator--(int)
    {
   
        Self tmp(*this);
        --(*this);
        return tmp;
    }
    bool operator!=(const Self& it)
    {
   
        return _ptr != it._ptr;
    }
};

4. set和map迭代器接口实现和适配

首先在红黑树以及set和map内部声明迭代器类型:

typedef rbtree_iterator<T, T&, T*> Iterator;
typedef rbtree_iterator<T, const T&, const T*> ConstIterator;
typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;

注意:这里typename的作用是告诉编译器Iterator和ConstIterator是类型名,缺失会编译报错。

然后在红黑树内部实现迭代器接口:

Iterator Begin()
{
   
    Node* cur = _root;
    while (cur && cur->_left)
    {
   
        cur = cur->_left;
    }
    return Iterator(cur, _root);
}

Iterator End()
{
   
    return Iterator(nullptr, _root);
}

ConstIterator Begin() const
{
   
    Node* cur = _root;
    while (cur && cur->_left)
    {
   
        cur = cur->_left;
    }
    return ConstIterator(cur, _root);
}

ConstIterator End() const
{
   
    return ConstIterator(nullptr, _root);
}

在set和map中,对以上接口进行封装调用:

iterator begin()
{
   
    return _rbtree.Begin();
}

iterator end()
{
   
    return _rbtree.End();
}

const_iterator begin() const
{
   
    return _rbtree.Begin();
}

const_iterator end() const
{
   
    return _rbtree.End();
}

迭代器实现完成之后,我们再对InsertFind函数的返回值进行迭代器适配修改:

pair<Iterator, bool> Insert(const T& data)
{
   
    if (_root == nullptr)
    {
   
        _root = new Node(data);
        _root->_col = BLACK;
        return {
    Iterator(_root,_root),true };
    }
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
   
        if(_kot(data) < _kot(cur->_data))
        {
   
            parent = cur;
            cur = cur->_left;
        }
        else if(_kot(data) > _kot(cur->_data))
        {
   
            parent = cur;
            cur = cur->_right;
        }
        else
        {
   
            return {
    Iterator(cur, _root), false };
        }
    }
    cur = new Node(data);
    if (_kot(data) < _kot(parent->_data))
    {
   
        parent->_left = cur;
    }
    else
    {
   
        parent->_right = 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
            {
   
                if (cur == parent->_left)
                {
   
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else
                {
   
                    RotateL(parent);
                    RotateR(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
            {
   
                if (cur == parent->_right)
                {
   
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else
                {
   
                    RotateR(parent);
                    RotateL(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                break;
            }
        }
    }
    _root->_col = BLACK;
    return {
    Iterator(cur,_root),true };
}
Iterator Find(const K& key)
{
   
    Node* cur = _root;
    while (cur)
    {
   
        if(key < _kot(cur->_data))
        {
   
            cur = cur->_left;
        }
        else if(key > _kot(cur->_data))
        {
   
            cur = cur->_right;
        }
        else
        {
   
            return Iterator(cur, _root);
        }
    }
    return Iterator(nullptr, _root);
}

5. set和map功能实现

之前我们已经实现了封装实现了set和map的迭代器接口,最后封装InsertFind接口,以及实现map的operator[ ]接口。

insert和find

直接对红黑树的Insert和Find函数进行封装调用即可:

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

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

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

map的operator[ ]

operator既支持元素插入,也支持修改,是因为其底层封装了insert方法,并且返回了Value值的引用。代码实现如下:

V& operator
{
   
    pair<iterator, bool> ret = insert({
    key,V() });
    return ret.first->second;
}

至此,通过对同一棵红黑树进行封装和适配,两种容器的基本功能已经实现完成。

三、测试

写一段代码测试我们实现的set和map:

void test_set()
{
   
    Set<int> s;
    s.insert(5);
    s.insert(3);
    s.insert(1);
    s.insert(7);
    s.insert(0);
    s.insert(9);
    s.insert(6);
    s.insert(4);
    for (auto& e : s)
    {
   
        cout << e << ' ';
    }
    cout << endl;
}

运行结果:

image.png

void test_map()
{
   
    string arr[] = {
    "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
    Map<string, int> m;
    for (auto& str : arr)
    {
   
        m[str]++;
    }
    for (auto& kv : m)
    {
   
        cout << kv.first << ':' << kv.second << endl;
    }
}

运行结果:

image.png

四、程序全部代码

set和map实现相关的全部代码如下:

RBTree.h

#include <iostream>
#include <utility>
using namespace std;

#pragma once
enum Color
{
   
    RED,
    BLACK
};

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

template<class T, class Ref, class Ptr>
struct rbtree_iterator
{
   
    typedef RBTreeNode<T> Node;
    typedef rbtree_iterator<T, Ref, Ptr> Self;
    Node* _ptr;
    Node* _root;
    rbtree_iterator(Node* ptr, Node* root)
        :_ptr(ptr)
        , _root(root)
    {
   }
    Ref operator*()
    {
   
        return _ptr->_data;
    }
    Ptr operator->()
    {
   
        return &(_ptr->_data);
    }
    Self& operator++()
    {
   
        if (_ptr->_right)
        {
   
            Node* rightMin = _ptr->_right;
            while (rightMin->_left)
            {
   
                rightMin = rightMin->_left;
            }
            _ptr = rightMin;
        }
        else
        {
   
            Node* cur = _ptr;
            Node* parent = cur->_parent;
            while (parent && parent->_right == cur)
            {
   
                cur = parent;
                parent = parent->_parent;
            }
            _ptr = parent;
        }
        return *this;
    }
    Self& operator--()
    {
   
        if (_ptr == nullptr)
        {
   
            Node* cur = _root;
            while (cur && cur->_right)
            {
   
                cur = cur->_right;
            }
            _ptr = cur;
        }
        else if (_ptr->_left)
        {
   
            Node* leftMax = _ptr->_left;
            while (leftMax->_right)
            {
   
                leftMax = leftMax->_right;
            }
            _ptr = leftMax;
        }
        else
        {
   
            Node* cur = _ptr;
            Node* parent = cur->_parent;
            while (parent && parent->_left == cur)
            {
   
                cur = parent;
                parent = parent->_parent;
            }
            _ptr = parent;
        }
        return *this;
    }
    Self operator++(int)
    {
   
        Self tmp(*this);
        ++(*this);
        return tmp;
    }
    Self operator--(int)
    {
   
        Self tmp(*this);
        --(*this);
        return tmp;
    }
    bool operator!=(const Self& it)
    {
   
        return _ptr != it._ptr;
    }
};

template<class K, class T, class KeyOfT>
class RBTree
{
   
    typedef RBTreeNode<T> Node;
public:
    typedef rbtree_iterator<T, T&, T*> Iterator;
    typedef rbtree_iterator<T, const T&, const T*> ConstIterator;
    RBTree() = default;
    RBTree(const RBTree<K, T, KeyOfT>& t)
    {
   
        _root = _Copy(t._root);
    }
    ~RBTree()
    {
   
        _Destroy(_root);
    }
    Iterator Begin()
    {
   
        Node* cur = _root;
        while (cur && cur->_left)
        {
   
            cur = cur->_left;
        }
        return Iterator(cur, _root);
    }
    Iterator End()
    {
   
        return Iterator(nullptr, _root);
    }
    ConstIterator Begin() const
    {
   
        Node* cur = _root;
        while (cur && cur->_left)
        {
   
            cur = cur->_left;
        }
        return ConstIterator(cur, _root);
    }
    ConstIterator End() const
    {
   
        return ConstIterator(nullptr, _root);
    }
    pair<Iterator, bool> Insert(const T& data)
    {
   
        if (_root == nullptr)
        {
   
            _root = new Node(data);
            _root->_col = BLACK;
            return {
    Iterator(_root,_root),true };
        }
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
   
            if (_kot(data) < _kot(cur->_data))
            {
   
                parent = cur;
                cur = cur->_left;
            }
            else if (_kot(data) > _kot(cur->_data))
            {
   
                parent = cur;
                cur = cur->_right;
            }
            else
            {
   
                return {
    Iterator(cur, _root), false };
            }
        }
        cur = new Node(data);
        if (_kot(data) < _kot(parent->_data))
        {
   
            parent->_left = cur;
        }
        else
        {
   
            parent->_right = 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
                {
   
                    if (cur == parent->_left)
                    {
   
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
   
                        RotateL(parent);
                        RotateR(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
                {
   
                    if (cur == parent->_right)
                    {
   
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
   
                        RotateR(parent);
                        RotateL(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
        }
        _root->_col = BLACK;
        return {
    Iterator(cur,_root),true };
    }
    Iterator Find(const K& key)
    {
   
        Node* cur = _root;
        while (cur)
        {
   
            if (key < _kot(cur->_data))
            {
   
                cur = cur->_left;
            }
            else if (key > _kot(cur->_data))
            {
   
                cur = cur->_right;
            }
            else
            {
   
                return Iterator(cur, _root);
            }
        }
        return Iterator(nullptr, _root);
    }
private:
    void RotateR(Node* parent)
    {
   
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        parent->_left = subLR;
        if (subLR) subLR->_parent = parent;
        Node* ppNode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;
        if (parent == _root)
        {
   
            _root = subL;
            subL->_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;
        Node* ppNode = parent->_parent;
        subR->_left = parent;
        parent->_parent = subR;
        if (parent == _root)
        {
   
            _root = subR;
            subR->_parent = nullptr;
        }
        else
        {
   
            if (ppNode->_left == parent) ppNode->_left = subR;
            else ppNode->_right = subR;
            subR->_parent = ppNode;
        }
    }
    Node* _Copy(Node* root, Node* parent = nullptr)
    {
   
        if (root == nullptr) return nullptr;
        Node* NewRoot = new Node(root->_kv);
        NewRoot->_col = root->_col;
        NewRoot->_parent = parent;
        NewRoot->_left = _Copy(root->_left, NewRoot);
        NewRoot->_right = _Copy(root->_right, NewRoot);
        return NewRoot;
    }
    void _Destroy(Node* root)
    {
   
        if (root == nullptr) return;
        _Destroy(root->_left);
        _Destroy(root->_right);
        delete root;
    }
    Node* _root = nullptr;
    KeyOfT _kot;
};

Set.h

#include "RBTree.h"
#pragma once
template<class K>
class Set
{
   
    struct SetKeyOfT
    {
   
        const K& operator()(const K& key)
        {
   
            return key;
        }
    };
public:
    typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
    typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;
    iterator begin()
    {
   
        return _rbtree.Begin();
    }
    iterator end()
    {
   
        return _rbtree.End();
    }
    const_iterator begin() const
    {
   
        return _rbtree.Begin();
    }
    const_iterator end() const
    {
   
        return _rbtree.End();
    }
    pair<iterator, bool> insert(const K& key)
    {
   
        return _rbtree.Insert(key);
    }
    iterator find(const K& key)
    {
   
        return _rbtree.Find(key);
    }
private:
    RBTree<K, const K, SetKeyOfT> _rbtree;
};

Map.h

#include "RBTree.h"
#pragma once
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>::ConstIterator const_iterator;
    iterator begin()
    {
   
        return _rbtree.Begin();
    }
    iterator end()
    {
   
        return _rbtree.End();
    }
    const_iterator begin() const
    {
   
        return _rbtree.Begin();
    }
    const_iterator end() const
    {
   
        return _rbtree.End();
    }
    pair<iterator, bool> insert(const pair<K, V>& kv)
    {
   
        return _rbtree.Insert(kv);
    }
    iterator find(const K& key)
    {
   
        return _rbtree.Find(key);
    }
    V& operator
    {
   
        pair<iterator, bool> ret = insert({
    key,V() });
        return ret.first->second;
    }
private:
    RBTree<K, pair<const K, V>, MapKeyOfT> _rbtree;
};

总结

本篇文章我们基于之前实现的红黑树模拟实现了set和map两种容器。不难发现,加入了模板和仿函数之后,两种容器能共用同一份红黑树代码,大大减少了代码冗余。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

相关文章
|
1月前
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
152 73
|
1月前
|
存储 算法 C++
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
80 3
|
2月前
|
编译器 容器
哈希表模拟封装unordered_map和unordered_set
哈希表模拟封装unordered_map和unordered_set
|
2月前
|
编译器 测试技术 计算机视觉
红黑树模拟封装map和set
红黑树模拟封装map和set
|
4月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
109 18
你对Collection中Set、List、Map理解?
|
4月前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
94 20
|
5月前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
123 3
【C++】map、set基本用法
|
5月前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
59 5
|
7月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
7月前
|
存储 Java API
【数据结构】map&set详解
本文详细介绍了Java集合框架中的Set系列和Map系列集合。Set系列包括HashSet(哈希表实现,无序且元素唯一)、LinkedHashSet(保持插入顺序的HashSet)、TreeSet(红黑树实现,自动排序)。Map系列为双列集合,键值一一对应,键不可重复,值可重复。文章还介绍了HashMap、LinkedHashMap、TreeMap的具体实现与应用场景,并提供了面试题示例,如随机链表复制、宝石与石头、前K个高频单词等问题的解决方案。
89 6
【数据结构】map&set详解

热门文章

最新文章

下一篇
oss创建bucket