【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;
};
AI 代码解读

二、set和map的模拟实现

1. 数据类型的设置

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

template<class K>
class Set
{
   
public:
private:
    RBTree<K, const K> _rbtree;
};
AI 代码解读
template<class K, class V>
class Map
{
   
public:
private:
    RBTree<K, pair<const K, V>> _rbtree;
};
AI 代码解读

我们将红黑树作为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)
    {
   }
};
AI 代码解读
template<class K, class T>
class RBTree
{
   
    typedef RBTreeNode<T> Node;
AI 代码解读

如此,当我们创建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;
    }
};
AI 代码解读

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

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

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

template<class K, class T, class KeyOfT>
class RBTree
AI 代码解读

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

RBTree<K, const K, SetKeyOfT> _rbtree;
RBTree<K, pair<const K, V>, MapKeyOfT> _rbtree;
AI 代码解读

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

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

KeyOfT _kot;//红黑树中新增一个仿函数对象便于调用
AI 代码解读
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;
}
AI 代码解读
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;
}
AI 代码解读

这样,尽管数据类型不同,但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;
};
AI 代码解读

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

迭代器的构造函数

rbtree_iterator(Node* ptr, Node* root)
    :_ptr(ptr)
    ,_root(root)
{
   }
AI 代码解读

*重载和->重载

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

Ref operator*()
{
   
    return _ptr->_data;
}
Ptr operator->()
{
   
    return &(_ptr->_data);
}
AI 代码解读

前置++

由于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;
}
AI 代码解读

前置--

前置--的实现逻辑与前置++基本相似(遵循 “ 反向中序遍历 ” ),但需要考虑已经遍历结束(_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;
}
AI 代码解读

后置++和--

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

Self operator++(int)
{
   
    Self tmp(*this);
    ++(*this);
    return tmp;
}
Self operator--(int)
{
   
    Self tmp(*this);
    --(*this);
    return tmp;
}
AI 代码解读

operator!=

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

bool operator!=(const Self& it)
{
   
    return _ptr != it._ptr;
}
AI 代码解读

迭代器全部实现代码:

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;
    }
};
AI 代码解读

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

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

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

注意:这里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);
}
AI 代码解读

在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();
}
AI 代码解读

迭代器实现完成之后,我们再对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 };
}
AI 代码解读
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);
}
AI 代码解读

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);
}
AI 代码解读
pair<iterator, bool> insert(const pair<K, V>& kv)
{
   
    return _rbtree.Insert(kv);
}

iterator find(const K& key)
{
   
    return _rbtree.Find(key);
}
AI 代码解读

map的operator[ ]

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

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

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

三、测试

写一段代码测试我们实现的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;
}
AI 代码解读

运行结果:

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;
    }
}
AI 代码解读

运行结果:

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;
};
AI 代码解读

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;
};
AI 代码解读

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;
};
AI 代码解读

总结

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

相关文章
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
145 73
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
77 3
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
115 1
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
深入浅出 C++ STL:解锁高效编程的秘密武器
C++ 标准模板库(STL)是现代 C++ 的核心部分之一,为开发者提供了丰富的预定义数据结构和算法,极大地提升了编程效率和代码的可读性。理解和掌握 STL 对于 C++ 开发者来说至关重要。以下是对 STL 的详细介绍,涵盖其基础知识、发展历史、核心组件、重要性和学习方法。
使用 entrySet 遍历 Map 类集合 KV
使用 entrySet 遍历 Map 类集合 KV
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
【Java集合类面试二十二】、Map和Set有什么区别?
该CSDN博客文章讨论了Map和Set的区别,但提供的内容摘要并未直接解释这两种集合类型的差异。通常,Map是一种键值对集合,提供通过键快速检索值的能力,而Set是一个不允许重复元素的集合。
【Java集合类面试五】、 如何得到一个线程安全的Map?
如何得到一个线程安全的Map的方法包括:使用Collections工具类将Map包装为线程安全,使用java.util.concurrent包下的ConcurrentHashMap,以及不推荐使用性能较差的Hashtable。

热门文章

最新文章