封装红⿊树实现mymap和myset

一. 源码及框架分析

SGI-STL30版本源代码,map和set的源代码在map/set/stl_map.h/stl_set.h/stl_tree.h等⼏个头⽂件
中。
map和set的实现结构框架核⼼部分截取出来如下:
        这是我们截取的几个比较重要的代码,图中看着很麻烦,我们先简化一下,删去一些没有用的东西。
        这是我们简化之后的图片。
        

通过上图对框架的分析,我们可以看到源码中rb_tree⽤了⼀个巧妙的泛型思想实现,rb_tree是实

现key的搜索场景,还是key/value的搜索场景不是直接写死的,⽽是由第⼆个模板参数Value决定

_rb_tree_node中存储的数据类型。

set实例化rb_tree时第⼆个模板参数给的是key,map实例化rb_tree时第⼆个模板参数给的是

pair<const key, T>,这样⼀颗红⿊树既可以实现key搜索场景的set,也可以实现key/value搜索场

景的map。

要注意⼀下,源码⾥⾯模板参数是⽤T代表value,⽽内部写的value_type不是我们我们⽇常

key/value场景中说的value,源码中的value_type反⽽是红⿊树结点中存储的真实的数据的类型。

rb_tree第⼆个模板参数Value已经控制了红⿊树结点中存储的数据类型,为什么还要传第⼀个模板

参数Key呢?尤其是set,两个模板参数是⼀样的,这是很多同学这时的⼀个疑问。要注意的是对于

map和set,find/erase时的函数参数都是Key,所以第⼀个模板参数是传给find/erase等函数做形

参的类型的。对于set⽽⾔两个参数是⼀样的,但是对于map⽽⾔就完全不⼀样了,map insert的

是pair对象,但是find和ease的是Key对象。

吐槽⼀下,这⾥源码命名⻛格⽐较乱,set模板参数⽤的Key命名,map⽤的是Key和T命名,⽽

rb_tree⽤的⼜是Key和Value,可⻅⼤佬有时写代码也不规范,乱弹琴。

我们可以发现,库中也是通过一棵树结构来实现set和map的,是通过第二个参数来区分的,那么可能有人要问了,如果是根据第二个参数区分的话,那么我们第一个参数的作用是什么呢?是不是就没有用了,不需要了呢?

当然不是的,对于我们的set,只有一个key,第一个参数和第二个参数可要可不要,但是对于我们的map就不一样了,我们的map的find和erase函数都是需要用到key的,第二个参数是pair,所以此时第一个参数就显得很重要了。

下面我们来模拟实现一下,可以帮助我们更好的理解。

二. 模拟实现map和set

2.1 实现出复⽤红⿊树的框架,并⽀持insert

参考源码框架,map和set复⽤之前我们实现的红⿊树。

我们这⾥相⽐源码调整⼀下,key参数就⽤K,value参数就⽤V,红⿊树中的数据类型,我们使⽤

T。

其次因为RBTree实现了泛型不知道T参数导致是K,还是pair<K, V>,那么insert内部进⾏插⼊逻辑

⽐较时,就没办法进⾏⽐较,因为pair的默认⽀持的是key和value⼀起参与⽐较,我们需要时的任

何时候只⽐较key,所以我们在map和set层分别实现⼀个MapKeyOfT和SetKeyOfT的仿函数传给

RBTree的KeyOfT,然后RBTree中通过KeyOfT仿函数取出T类型对象中的key,再进⾏⽐较,具体

细节参考如下代码实现。

        这是我们实现的一个树结构。

        myset

        

mymap

        此时我们就发现了一个问题,我们在插入和find等函数中,我们的map需要kv.first的比较,但是,我们传过去的V是一个pair类型的对象,此时是无法完成比较的,两个pair类型的参数的比较,底层的比较逻辑是无法满足我们的需求的,即使能满足,我们的find函数一个pair类型和一个K类型的比较也是无法完成的,此时有人可能会想到用仿函数直接写一个比较,但是此时你的map的仿函数第二个参数的类型也是不确定的,你还是没法写,因为insert中第二个参数类型是pair,find中第二个参数类型又变成K了,此时还是无法完成的,所以,我们不应该直接通过仿函数来比较,而是通过仿函数拿到kv.first的值即可。下面我们来实现一下。

        仿函数的实现

        大家可以看一下,我们增加了仿函数,我们的RBTree类也要多一个函数模板参数了。

        

        此时我们修改一下insert和find函数即可。

        

        这是我们插入函数修改的地方,直接使用仿函数来获取我们所需要的值。

        

        这是我们的find函数,此时就完成了我们需要的操作。

2.2 ⽀持iterator的实现

iterator实现思路分析

iterator实现的⼤框架跟list的iterator思路是⼀致的,⽤⼀个类型封装结点的指针,再通过重载运算

符实现,迭代器像指针⼀样访问的⾏为。

这⾥的难点是operator++和operator--的实现。之前使⽤部分,我们分析了,map和set的迭代器⾛

的是中序遍历,左⼦树->根结点->右⼦树,那么begin()会返回中序第⼀个结点的iterator也就是10

所在结点的迭代器。

迭代器++的核⼼逻辑就是不看全局,只看局部,只考虑当前中序局部要访问的下⼀个结点。

迭代器++时,如果it指向的结点的右⼦树不为空,代表当前结点已经访问完了,要访问下⼀个结点

是右⼦树的中序第⼀个,⼀棵树中序第⼀个是最左结点,所以直接找右⼦树的最左结点即可。

迭代器++时,如果it指向的结点的右⼦树空,代表当前结点已经访问完了且当前结点所在的⼦树也

访问完了,要访问的下⼀个结点在当前结点的祖先⾥⾯,所以要沿着当前结点到根的祖先路径向上

找。

如果当前结点是⽗亲的左,根据中序左⼦树->根结点->右⼦树,那么下⼀个访问的结点就是当前结

点的⽗亲;如下图:it指向25,25右为空,25是30的左,所以下⼀个访问的结点就是30。

如果当前结点是⽗亲的右,根据中序左⼦树->根结点->右⼦树,当前当前结点所在的⼦树访问完

了,当前结点所在⽗亲的⼦树也访问完了,那么下⼀个访问的需要继续往根的祖先中去找,直到找

到孩⼦是⽗亲左的那个祖先就是中序要问题的下⼀个结点。如下图:it指向15,15右为空,15是10

的右,15所在⼦树话访问完了,10所在⼦树也访问完了,继续往上找,10是18的左,那么下⼀个

访问的结点就是18。

end()如何表⽰呢?如下图:当it指向50时,++it时,50是40的右,40是30的右,30是18的右,18

到根没有⽗亲,没有找到孩⼦是⽗亲左的那个祖先,这是⽗亲为空了,那我们就把it中的结点指针

置为nullptr,我们⽤nullptr去充当end。需要注意的是stl源码空,红⿊树增加了⼀个哨兵位头结点

做为end(),这哨兵位头结点和根互为⽗亲,左指向最左结点,右指向最右结点。相⽐我们⽤

nullptr作为end(),差别不⼤,他能实现的,我们也能实现。只是--end()判断到结点时空,特殊处

理⼀下,让迭代器结点指向最右结点。具体参考迭代器--实现。

迭代器--的实现跟++的思路完全类似,逻辑正好反过来即可,因为他访问顺序是右⼦树->根结点->

左⼦树,具体参考下⾯代码实现。

set的iterator也不⽀持修改,我们把set的第⼆个模板参数改成const K即可, RBTree<K,

const K, SetKeyOfT> _t;

map的iterator不⽀持修改key但是可以修改value,我们把map的第⼆个模板参数pair的第⼀个参

数改成const K即可, RBTree<K, pair<const K, V>, MapKeyOfT> _t;

        我们先直接给出代码再来解释。

        

        我们都知道,我们想使用迭代器,是需要拥有两个函数的begin()和end()函数,还有一些++和--的一些基本迭代器的操作,首先这些*和->还有!=和==操作都是很简单的,接下来我们重点讲一下++和--的操作,我们先来看++操作。

        

        我们来看这个图,所谓的++操作就是在中序的基础上往后面移动一位,这是毫无疑问的,接下来我们来看它有几种情况,当我们的这个结点存在右孩子的时候,这是一种情况,我们看10这个结点,它存在右孩子,那么它中序的下一个结点怎么找呢?当然就是他的右孩子的最左结点了,这就是中序的下一位,这是很好理解的,但是如果它没有右孩子呢?这时候该怎么办呢?当指向15的时候是不存在右孩子的,这时候我们就要从祖先结点中去找我们的中序的下一位了,这是毫无疑问的,15这个我们从图中很容易找到,下一个结点是18,18是15的爷爷,那么是不是不存在右孩子,爷爷就是我们中序的下一位呢?当然不是了,你再看看35,不存在右孩子,但是此时他的父亲是它中序的下一位,而不是爷爷,显然我们上面说的是错误的。     

        那么有什么规律呢?我们可以看出,我们结束的条件是我们的cur==parent->left,当cur==parent-》right的时候是继续往上面走的,这时候我们的这个问题就得到解决了,当时如果永远不满足这个条件该怎么结束呢?比如我们的50,他是永远不会满足我们的cur==parent->left,那么我们就要再加一个条件,就是parent存在,此时问题就得到了解决了。

        ++得到了解决,--的做法是一样的,大家可以自己画个图完成一下代码的书写。

        

Begin和End方法

        这是我们的所写的Begin和End方法,因为End指向的是最后一个元素的下一个位置,所以我们直接传入nullptr就行,Begin我们找到树的最左结点即可,都是很简单实现的。

        

        我们实现了基本的迭代器,但是还存在const类型的迭代器呀,我们应该怎么实现呢?

Const_Iterator

        我们要怎么实现这个类型的迭代器呢?难道是重新写一个结构体来实现const类型的吗?当然不是了,为了使我们的代码的复用性增高,我们只需要增加两个模板参数即可完成操作。

        下面我来实现一下。

        

        我们只需要这样改一下即可,增加了两个模板参数来区分我们的T&,T*和const T& , const T*。

        这样改一下就行了。

        

        这是我们在我们的mymap中实现的begin和end方法,调用一下RBTree中的即可。

        有人可能会问,这个typename的作用是什么呢?

        

在这句代码中,typename 的核心作用是告诉编译器:RBTree<K, const K, SetKeyOfT>::Iterator 是一个类型(而非成员变量或其他实体),从而避免编译歧义。

具体原因分析:

C++ 模板中,当通过类模板的嵌套依赖名称(即依赖于模板参数的内部名称,如 RBTree<...>::Iterator)引用类型时,编译器在解析阶段无法确定该名称是 “类型” 还是 “非类型”(如静态成员变量)。

例如,对于 RBTree<K, const K, SetKeyOfT>::Iterator

  • RBTree 是一个类模板,其模板参数 K 是不确定的(依赖于外部模板参数);
  • Iterator 是 RBTree 内部的一个嵌套类型(迭代器类型),但编译器在未实例化模板前,无法仅凭名称判断它是类型还是其他成员(比如静态变量)。

此时,必须用 typename 显式声明:这个嵌套依赖名称是一个类型,编译器才能正确解析。

反例:缺少 typename 会导致什么?

如果去掉 typename

cpp

运行

typedef RBTree<K, const K, SetKeyOfT>::Iterator iterator;  // 错误

编译器可能会将 Iterator 误认为是 RBTree<...> 的静态成员变量,从而报 “语法错误” 或 “无法将非类型转换为类型” 的错误。

        底层红黑树的实现,它还加了一个哨兵位的头结点,左孩子是begin(),右孩子是最后一个结点。

        哨兵位结点的作用:

红黑树中哨兵位头结点(Sentinel Node) 是一个虚拟节点(不存储实际数据),核心作用是简化边界条件处理、统一代码逻辑、提升操作效率,避免因空指针(nullptr)判断导致的复杂分支。

一、核心作用(分点解析)

1. 替代空指针(nullptr),消除边界判断

红黑树的节点操作(插入、删除、旋转)中,大量涉及 “父节点、左 / 右子节点是否为空” 的判断(如叶子节点的子节点、根节点的父节点)。

  • 没有哨兵时:需频繁判断 node->_left == nullptr 或 node->_parent == nullptr,代码冗余且易出错;

  • 有哨兵时:将所有 “空指针位置” 统一指向哨兵节点(如叶子节点的左 / 右子节点、根节点的父节点均为哨兵),无需再判断空指针,直接操作哨兵即可。

2. 统一根节点与普通节点的操作逻辑

根节点的特殊之处在于 “无父节点”,删除或旋转时需单独处理 “根节点更新” 逻辑。

  • 哨兵节点作为根节点的父节点:根节点的操作(如旋转后更新父指针)可与普通节点完全一致,无需额外编写根节点专属代码。

3. 简化迭代器遍历的边界处理

红黑树迭代器的 Begin()(最左节点)和 End()(尾后位置)需要明确边界:

  • 哨兵节点可作为 End() 的指向(尾后迭代器),无需返回 nullptr

  • 迭代器移动时(如 operator++ 到尾后),直接指向哨兵,判断迭代器是否相等时只需对比节点指针(而非判断 nullptr),逻辑更统一。

2.3 map⽀持[]

        根据我们前面所学的[]的实现要借助Insert函数,我们要把返回值改成pair<iterator,bool>类型的。

        

        我们改完之后就可以完成我们的[]的书写了。

        我们可以很容易的想到,bool值的利用就是来判断我们是否插入成功,而迭代器是存放我们插入元素的迭代器的,使我们可以访问到它。

        看一下下面的代码就知道了

        

        这是我们在我们写的map中实现的[]操作符的使用,我们可以使用[]来访问里面的元素,当这个元素不存在的时候进行插入操作,存在的时候就访问,这里的严格来说应该是->->才能访问的到second的,因为第一个箭头是我们得到了

        这个地址,想访问里面的元素,还需要一个箭头来操作,但是我们编译器优化之后,只需要一个箭头即可。

        这里我们的map[]就完成了。

        测试一下我们的核心功能就结束了。

        

        

我们发现符合我们的预期,我们的代码书写就完成了,这个代码有点麻烦,我附一下源代码吧。

附录:

RBTree类

#pragma once

// 枚举值表示颜色
enum Colour
{
    RED,
    BLACK
};

// 这里我们默认按key/value结构实现
template<class T>
struct RBTreeNode
{
    // 这里更新控制平衡也要加入parent指针
    T _data;
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    Colour _col;

    RBTreeNode(const T& data)
        :_data(data)
        , _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
    {
    }
};

template<class T, class Ref, class Ptr>
struct __TreeIterator
{
    typedef RBTreeNode<T> Node;
    typedef __TreeIterator<T, Ref, Ptr> Self;
    Node* _node;
    Node* _root;

    __TreeIterator(Node* node, Node* root)
        :_node(node)
        , _root(root)
    {
    }

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

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

    Self& operator++()
    {
        if (_node->_right)
        {
            // 右子树的最左节点
            _node = _node->_right;
            while (_node->_left)
            {
                _node = _node->_left;
            }
        }
        else
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_right)
            {
                cur = parent;
                parent = parent->_parent;
            }

            _node = parent;
        }

        return *this;
    }

    Self& operator--()
    {
        if (_node == nullptr) // end()
        {
            // --end(),特殊处理,走到中序最后一个结点,整棵树的最右结点
            Node* rightMost = _root;
            while (rightMost && rightMost->_right)
            {
                rightMost = rightMost->_right;
            }
            _node = rightMost;
        }
        else if (_node->_left)
        {
            // 左子树不为空,中序左子树最后一个
            Node* rightMost = _node->_left;
            while (rightMost->_right)
            {
                rightMost = rightMost->_right;
            }
            _node = rightMost;
        }
        else
        {
            // 孩子是父亲右的那个祖先
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_left)
            {
                cur = parent;
                parent = cur->_parent;
            }
            _node = parent;
        }

        return *this;
    }

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

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

template<class K, class T, class KeyOfT>
class RBTree
{
    typedef RBTreeNode<T> Node;
public:
    typedef __TreeIterator<T, T&, T*> Iterator;
    typedef __TreeIterator<T, const T&, const T*> ConstIterator;

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

        return Iterator(minLeft, _root);
    }

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

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

        return ConstIterator(minLeft, _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;

        KeyOfT kot;
        while (cur)
        {
            if (kot(cur->_data) < kot(data))
            {
                parent = cur;
                cur = cur->_right;
            }
            //else if (kot(cur->_data) > kot(data))
            else if (kot(data) < kot(cur->_data))
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return { Iterator(cur, _root), false };
            }
        }

        cur = new Node(data);
        Node* newnode = cur;
        cur->_col = RED;
        if (kot(parent->_data) < kot(data))
        {
            parent->_right = cur;
        }
        else
        {
            parent->_left = cur;
        }
        cur->_parent = parent;

        while (parent && parent->_col == RED)
        {
            Node* grandfather = parent->_parent;
            if (parent == grandfather->_left)
            {
                //   g
                // p   u
                Node* uncle = grandfather->_right;
                // 叔叔存在且为红
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;

                    // 继续往上处理
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    // 叔叔不存在或者存在且为黑
                    //      g
                    //   p     u
                    //c
                    if (cur == parent->_left)
                    {
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        //      g
                        //   p     u
                        //      c
                        RotateL(parent);
                        RotateR(grandfather);
                        grandfather->_col = RED;
                        cur->_col = BLACK;
                    }

                    break;
                }
            }
            else
            {   //   g
                // u   p
                Node* uncle = grandfather->_left;
                // 叔叔存在且为红,-》变色即可
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;

                    // 继续往上处理
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else // 叔叔不存在,或者存在且为黑
                {
                    // 情况二:叔叔不存在或者存在且为黑
                    // 旋转+变色
                    //      g
                    //   u     p
                    //            c
                    if (cur == parent->_right)
                    {
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        //      g
                        //   u     p
                        //      c
                        RotateR(parent);
                        RotateL(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
        }

        _root->_col = BLACK;

        return { Iterator(newnode, _root), true };
    }

    Iterator Find(const K& key)
    {
        KeyOfT kot;
        Node* cur = _root;
        while (cur)
        {
            if (kot(cur->_data) < key)
            {
                cur = cur->_right;
            }
            else if (kot(cur->_data) > key)
            {
                cur = cur->_left;
            }
            else
            {
                return Iterator(cur, _root);
            }
        }

        return End();
    }

    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;

        parent->_left = subLR;
        subL->_right = parent;

        if (subLR)
            subLR->_parent = parent;

        Node* parentParent = parent->_parent;
        parent->_parent = subL;

        //if (parentParent == nullptr)
        if (parent == _root)
        {
            _root = subL;
            subL->_parent = nullptr;
        }
        else
        {
            if (parentParent->_left == parent)
            {
                parentParent->_left = subL;
            }
            else
            {
                parentParent->_right = subL;
            }

            subL->_parent = parentParent;
        }
    }

    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        parent->_right = subRL;
        if (subRL)
            subRL->_parent = parent;
        Node* parentParent = parent->_parent;
        subR->_left = parent;
        parent->_parent = subR;
        if (parentParent == nullptr)
        {
            _root = subR;
            subR->_parent = nullptr;
        }
        else
        {
            if (parent == parentParent->_left)
            {
                parentParent->_left = subR;
            }
            else
            {
                parentParent->_right = subR;
            }
            subR->_parent = parentParent;
        }
    }

    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }

    void _InOrder(Node* root)
    {
        if (root == nullptr)
        {
            return;
        }

        _InOrder(root->_left);
        cout << root->_kv.first << " ";
        _InOrder(root->_right);
    }

    // 前序递归遍历
    bool Check(Node* root, int blackNum, const int refNum)
    {
        if (root == nullptr)
        {
            // 前序遍历走到空时,意味着一条路径走完了
            //cout << blackNum << endl;
            if (refNum != blackNum)
            {
                cout << "存在黑色结点的数量不相等的路径" << endl;
                return false;
            }
            return true;
        }

        // 检查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲就方便多了
        if (root->_col == RED && root->_parent && root->_parent->_col == RED)
        {
            cout << root->_kv.first << "存在连续的红色结点" << endl;
            return false;
        }

        if (root->_col == BLACK)
        {
            blackNum++;
        }

        return Check(root->_left, blackNum, refNum)
            && Check(root->_right, blackNum, refNum);
    }

    bool IsBalanceTree()
    {
        if (_root == nullptr)
            return true;

        if (_root->_col == RED)
            return false;

        // 参考值
        int refNum = 0;
        Node* cur = _root;
        while (cur)
        {
            if (cur->_col == BLACK)
            {
                ++refNum;
            }
            cur = cur->_left;
        }

        return Check(_root, 0, refNum);
    }

private:
    Node* _root = nullptr;
};

mymap:

#pragma once

#include"RBTree.h"

namespace qrh
{
    template<class K, class V>
    class map
    {
        struct MapKeyOfT
        {
            const K& operator()(const pair<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 _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>& kv)
        {
            return _t.Insert(kv);
        }

        V& operator[](const K& key)
        {
            pair<iterator, bool> ret = _t.Insert({ key, V() });
            return ret.first->second;
        }
    private:
        RBTree<K, pair<const K, V>, MapKeyOfT> _t;
    };
}

myset:

#pragma once

#include"RBTree.h"

namespace qrh
{
    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 _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);
        }

    private:
        RBTree<K, const K, SetKeyOfT> _t;
    };
}

Test:

#define _CRT_SECURE_NO_WARNINGS 1

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

#include"RBTree.h"
// 测试代码
#include"myset.h"
#include"mymap.h"

void Print(const qrh::set<int>& s)
{
    for (auto& e : s)
    {
        //e++;
        cout << e << " ";
    }
    cout << endl;
}

void test_set()
{
    qrh::set<int> s;
    s.insert(4);
    s.insert(1);
    s.insert(3);
    s.insert(6);
    s.insert(16);
    s.insert(6);
    s.insert(36);
    s.insert(-6);
    s.insert(9);

    qrh::set<int>::iterator it = s.begin();
    while (it != s.end())
    {
        //*it = 1;
        cout << *it << " ";
        ++it;
    }
    cout << endl;

    for (auto& e : s)
    {
        //e++;
        cout << e << " ";
    }
    cout << endl;

    Print(s);

    it = s.end();
    while (it != s.begin())
    {
        --it;
        cout << *it << " ";
    }
    cout << endl;
}

void test_map()
{
    qrh::map<string, string> dict;
    dict.insert({ "sort", "排序" });
    dict.insert({ "left", "左边" });
    dict.insert({ "right", "右边" });

    qrh::map<string, string>::iterator it = dict.begin();
    while (it != dict.end())
    {
        //it->first += 'x';
        it->second += 'x';

        cout << it->first << ":" << it->second << endl;
        ++it;
    }
    cout << endl;
}

int main()
{
    test_set();
    test_map();

    return 0;
}

<think>好的,我现在需要帮助用户配置GRE隧道与IPSec加密,实现总部数据中心的内网互联,同时详细指定OSPF、加密算法安全策略。首先,我需要回忆一下GREIPSec的基本配置步骤,以及如何将它们结合起来使用。用户还提到了使用环回接口配置GRE隧道,承载OSPF区域0,所以这部分也需要特别注意。 首先,GRE隧道的配置通常涉及定义隧道的源目的地址,这里用户提到使用环回接口,所以可能需要将隧道的源目的设置为环回接口的IP地址。然后,配置OSPF在隧道接口上,确保区域0的正确宣告。接下来是IPSec部分,用户指定了ESP传输模式、ISAKMP策略使用3DESMD5,预共享密钥,以及DH组2。转换集是myset,使用esp-3desesp-md5-hmac,感兴趣流的ACL是103,加密图名为mymap。 我需要逐步分解这些配置步骤,确保每个部分都正确无误。首先配置GRE隧道,使用环回接口作为源目的地址,分配隧道接口的IP地址,并启用OSPF。然后配置IPSec,包括IKE阶段1的策略,设置加密算法为3DES,散列为MD5,DH组2,预共享密钥。然后是IPSec转换集,定义ESP使用的加密认证算法。接着定义感兴趣流ACL 103,确定哪些流量需要被加密。最后创建加密图,将转换集ACL绑定,并应用到物理接口上。 需要注意的潜在问题包括:环回接口的可达性,确保隧道的源目的地址能够互相通信;OSPF邻居关系能否在隧道接口上正确建立;IPSec的传输模式是否适用于GRE隧道,因为GRE通常是点对点的,传输模式可能更高效;ACL的配置是否正确,确保匹配需要加密的流量;加密图的应用是否正确到物理接口上,而不是隧道接口。 用户可能还需要验证配置是否生效,比如检查GRE隧道状态,OSPF邻居是否建立,IPSec SA是否协商成功,以及流量是否被正确加密路由。可能需要使用一些诊断命令,如display ike sa、display ipsec sa、ping测试等。 另外,用户提供的引用中提到了GRE隧道需要建立OSPF邻居,并且在IPSec配置中分为两个阶段,验证对等体身份定义流量。因此,在配置时需要确保这两个阶段的设置正确,特别是预共享密钥感兴趣流的ACL是否正确定义。 现在,我需要将以上思考整理成一个结构化的回答,分步骤指导用户进行配置,确保每个部分都清晰,并引用提供的参考内容中的相关信息来支持步骤的正确性。</think>### 配置GRE over IPSec实现内网互联的步骤 #### 一、配置环回接口与GRE隧道 1. **配置环回接口(两端设备)** 在总部数据中心设备上创建环回接口,并分配固定IP地址(如总部用$10.1.1.1/32$,数据中心用$10.1.1.2/32$): ```bash interface LoopBack0 ip address 10.1.1.1 255.255.255.255 ``` 2. **建立GRE隧道接口** 在两端设备上创建GRE隧道接口,并指定源目的为环回接口IP: ```bash interface Tunnel10 ip address 172.16.17.1 255.255.255.252 # 隧道IP需在同一网段 tunnel-protocol gre source 10.1.1.1 # 总部环回接口IP destination 10.1.1.2 # 数据中心环回接口IP ``` *注:需确保环回接口之间路由可达(如通过公网静态路由)[^1]。* 3. **启用OSPF区域0** 在GRE隧道接口上激活OSPF,宣告内网网段: ```bash ospf enable 1 area 0.0.0.0 network 192.168.0.0 0.0.255.255 area 0 # 假设内网为192.168.0.0/16 ``` --- #### 二、配置IPSec加密GRE流量 1. **定义IKE阶段1参数** 设置ISAKMP策略,匹配3DES、MD5、DH组2预共享密钥: ```bash ike proposal 10 encryption-algorithm 3des-cbc integrity-algorithm md5 dh group2 sa duration 86400 ike keychain ruijie pre-shared-key address 10.1.1.2 key YourSecretKey # 对端环回接口IP ``` 2. **配置IPSec转换集** 定义ESP加密认证算法(需与IKE阶段2匹配): ```bash ipsec transform-set myset esp encryption-algorithm 3des esp integrity-algorithm md5 ``` 3. **定义感兴趣流ACL 103**
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值