C++红黑树模拟实现map和set(2)

简介: C++红黑树模拟实现map和set(2)

二、红黑树的迭代器


迭代器本质上是指针的一个封装的类,其底层就是指针;好处是可以方便遍历,是数据结构的底层实现与用户透明


对于string,vector,list等容器,其本身的结构上是比较简单的,迭代器的实现也很简单;但是对于二叉树结构的红黑树来说需要考虑很多的问题


1、begin()与end()


STL明确规定:begin()与end()代表的是一段前闭后开的区间


对红黑树进行中序遍历后,可以得到一个有序的序列,因此begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置即nullptr


  • 示图:


image.png


2、operator++()与operator–()


找到begin()和end()之后,要遍历红黑树最主要的还是能找到要遍历的下一个节点


红黑树的中序遍历是有序的,也就是说:当一个结点的正向迭代器进行++操作后,应该根据红黑树中序遍历的序列找到当前结点的下一个结点


实现++逻辑:

对于中遍历到当前节点来说,该节点的左子树应该是已经被遍历过了,所以只需要考虑右子树


如果当前结点的右子树不为空,则++操作后应该找到其右子树当中的最左结点


如果当前结点的右子树为空,则该子树已经被遍历完了,则++操作后应该在该结点的祖先结点中找到孩子不在父亲右的祖先


说明:

如果是孩子节点在父亲的左,说明父亲的右节点没有遍历


如果孩子在父亲的右,说明以父亲节点为根的子树已经遍历完了,还需要继续向上找


如果一直向上遍历到nullptr说明整颗树已经遍历完了(end()返回的就是nullptr)


注:怎么看该节点的右节点遍历过没有,这里需要一个指针记录上一次经过的节点地址,进行比较地址就行了


实现++代码:


Self& operator++()
{
    if (_node->_right)//右子节点存在
    {
        //找到右子树中最左节点
        Node* cur = _node->_right;
        while (cur->_left)
        {
            cur = cur->_left;
        }
        _node = cur;
    }
    else//右子节点不存在,向上找
    {
        Node* cur = _node;//记录走过的节点
        Node* parent = _node->_parent;
        while (parent && parent->_right == cur)
        {
            cur = parent;
            parent = parent->_parent;
        }
        _node = parent;
    }
    return *this;
}


注:对于–来说可以参考++的实现,逻辑是完全相反的


  • 实现–代码:


Self& operator--()
{
    if (_node->_left)//左子节点存在
    {
        //找左子树中的最右节点
        Node* cur = _node->_left;
        while (cur->_right)
        {
            cur = cur->_right;
        }
        _node = cur;
    }
    else//左子节点不存在
    {
        Node* cur = _node;
        Node* parent = _node->_parent;
        while (parent && parent->_left == cur)
        {
            cur = parent;
            parent = parent->_parent;
        }
        _node = parent;
    }
    return *this;
}


3、正反迭代器的实现


对于正向迭代器与反向迭代器的区别就是:begin()指向的位置不一样;迭代器的++和–的实现相反,但本质上还是差不多的


所以实现正向迭代器后,我们可以直接使用适配器,在正向迭代器的基础上,对其接口进行封装达到反向迭代器的效果


正向迭代器实现代码:


template<class T, class Ref, class Ptr>
struct _TreeIterator
{
    //声明类型,便于反向迭代器对类型的提取
  typedef Ref reference;
  typedef Ptr pointer;
  typedef RBTreeNode<T> Node;
  typedef _TreeIterator<T, Ref, Ptr> Self;
  Node* _node;
  _TreeIterator(Node* node)
    :_node(node)
  {}
  Ref operator*()
  {
    return _node->_data;
  }
  Ptr operator->()
  {
    return &_node->_data;
  }
  bool operator==(const Self& it)const
  {
    return _node == it._node;
  }
  bool operator!= (const Self& it)const
  {
    return _node != it._node;
  }
  Self& operator++()
  {
    if (_node->_right)
    {
      Node* cur = _node->_right;
      while (cur->_left)
      {
        cur = cur->_left;
      }
      _node = cur;
    }
    else
    {
      Node* cur = _node;
      Node* parent = _node->_parent;
      while (parent && parent->_right == cur)
      {
        cur = parent;
        parent = parent->_parent;
      }
      _node = parent;
    }
    return *this;
  }
  Self& operator--()
  {
    if (_node->_left)
    {
      Node* cur = _node->_left;
      while (cur->_right)
      {
        cur = cur->_right;
      }
      _node = cur;
    }
    else
    {
      Node* cur = _node;
      Node* parent = _node->_parent;
      while (parent && parent->_left == cur)
      {
        cur = parent;
        parent = parent->_parent;
      }
      _node = parent;
    }
    return *this;
  }
};


  • 反向迭代器实现代码:


//适配器构造反向迭代器
template<class Iterator>
struct ReverseIterator
{
  //类型未实例化,无法取出里面的类型,此时需要使用typename:告诉编译器等实例化后再到类里面找对应的类型
  typedef typename Iterator::reference Ref;
  typedef typename Iterator::pointer Ptr;
  typedef ReverseIterator<Iterator> Self;
  Iterator _it;
  ReverseIterator(Iterator it)
    :_it(it)
  {}
  //在正向迭代器接口上进行封装复用   
  Ref operator*()
  {
    return *_it;
  }
  Ptr operator->()
  {
    return _it.operator->();
  }
  bool operator==(const Self& it)const
  {
    return it._it==_it;
  }
  bool operator!= (const Self& it)const//两个const
  {
    return _it != it._it;
  }
  Self& operator++()
  {
    --_it;
    return *this;
  }
  Self& operator--()
  {
    ++_it;
    return *this;
  }
};


相关文章
|
1月前
|
存储 自然语言处理 C++
map和set的简单介绍
map和set的简单介绍
20 1
|
2天前
|
存储 JavaScript 索引
js开发:请解释什么是ES6的Map和Set,以及它们与普通对象和数组的区别。
ES6引入了Map和Set数据结构。Map的键可以是任意类型且有序,与对象的字符串或符号键不同;Set存储唯一值,无重复。两者皆可迭代,支持for...of循环。Map有get、set、has、delete等方法,Set有add、delete、has方法。示例展示了Map和Set的基本操作。
16 3
|
2天前
|
存储 搜索推荐 C++
【C++高阶(二)】熟悉STL中的map和set --了解KV模型和pair结构
【C++高阶(二)】熟悉STL中的map和set --了解KV模型和pair结构
|
22天前
|
存储 JavaScript 前端开发
set和map的区别
set和map的区别
29 4
|
29天前
|
存储 C++
【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗)
【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗)
|
1月前
|
存储 算法 C语言
【C++入门到精通】C++入门 —— map & multimap (STL)
之前我们学习了C++的基础和一些概念,现在将探讨重要的STL组件——map与multimap。map是关联容器,提供有序键值对存储,基于红黑树,支持高效查找、插入和删除。每个键唯一对应一个值。multimap则允许键的重复。两者都提供迭代器支持,但map的键是唯一的,而multimap允许键重复,插入和查找效率不同。更多详情,请查阅官方文档。祝学习愉快!
12 0
|
1月前
|
存储 算法 C++
【C++ map结构 】std::map 和 std::unordered_map 在使用上的差异
【C++ map结构 】std::map 和 std::unordered_map 在使用上的差异
22 0
|
1月前
|
存储 编译器 容器
用红黑树封装实现map和set
用红黑树封装实现map和set
14 0
|
1月前
|
存储 算法 C++
【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程
【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程
85 1
|
1月前
|
存储 安全 Java
java集合框架及其特点(List、Set、Queue、Map)
java集合框架及其特点(List、Set、Queue、Map)