【C++】set模拟实现

简介: C++中的`set`是STL提供的一种关联容器,用于存储唯一元素并自动按特定顺序(默认升序)排序。其内部通过红黑树实现,保证了高效的插入、删除和查找操作,时间复杂度均为O(log n)。`set`支持迭代器遍历,提供了良好的数据访问接口。

前言:

C++中的set是标准模板库(STL)中的一种关联容器,它存储了一组唯一的元素,并且这些元素是按照特定的顺序(默认是升序)进行排序的

set的介绍

C++中的set是标准模板库(STL)中的一种关联容器,它存储了一组唯一的元素,并且这些元素是按照特定顺序(默认是升序)排序的。set内部使用红黑树这种平衡二叉搜索树来组织元素,这使得set能够提供对数时间复杂度的查找、插入和删除操作。

set的特点

  • 唯一性:set中的元素是唯一的,插入重复元素时,新元素不会被添加到容器中。
  • 自动排序:set会自动对元素进行排序,不需要用户手动维护元素的顺序。
  • 高效的查找、插入和删除操作:由于set内部通常使用红黑树实现,这些操作的时间复杂度为对数级别(O(log n))。
  • 迭代器稳定性:在set中插入或删除元素不会使现有的迭代器失效,除了指向被删除元素的迭代器

set的实现(set的底层是红黑树

set的结构

  • 由于set和map是公用一棵树,set是K 、map是KV的结构,为了适应map的结构,set做出一定的改变
  • 内部类是为了取到set内所存储的数据
  • 在set的map中 set所存储的是key而map是pair
template<class K>
    class set
    {
   
        struct setofkey
        {
   
            const K& operator()(const K& key)
            {
   
                return key;
            }
        };
        public:
        private:
        RBTree <K, K, setofkey> _t;
    };

set的插入

这是红黑树的底层插入,大体上和红黑树是一样的

  • 寻找插入位置,进行插入。

  • 调整红黑树,保持红黑树的结构

  • 第一行的iterator是普通迭代器,而return返回的是const迭代器。在迭代器的封装的时候就要写iterator的构造函数

  • pair<iterator,bool> insert(const K& key)
    {
         
        pair<typename RBTree <K, K, setofkey>::iterator, bool> ret = _t.insert(key);
    
        return pair<iterator, bool>(ret.first, ret.second);
    }
    
pair<iterator,bool> insert(const T& data)
{
   
    if (_root == nullptr)
    {
   
        _root = new Node(data);
        _root->_col = BLACK;
        return make_pair(iterator(_root),true);
    }
    Node* parent = nullptr;
    Node* cur = _root;

    keyofT kot;

    while (cur)
    {
   
        if (kot(data) > kot(cur->_data))
        {
   
            parent = cur;
            cur = cur->_right;
        }
        else if (kot(data) < kot(cur->_data))
        {
   
            parent = cur;
            cur = cur->_left;
        }
        else
        {
   
            return make_pair(iterator(cur), false);
        }
    }

    cur = new Node(data);
    Node* newnode = cur;
    cur->_col = RED;

    if (kot(data) < kot(parent->_data))
    {
   
        parent->_left = cur;
    }
    else
    {
   
        parent->_right = cur;
    }
    cur->_parent = parent;

    while (parent && parent->_col == RED)
    {
   
        Node* grandparent = parent->_parent;
        if (parent == grandparent->_left)
        {
   
            Node* uncle = grandparent->_right;
            if (uncle && uncle->_col == RED)
            {
   
                parent->_col = uncle->_col = BLACK;
                grandparent->_col = RED;
                cur = grandparent;
                parent = cur->_parent;
            }
            else//uncle is not or black
            {
   
                if (cur == parent->_left)
                {
   
                    RotateR(grandparent);
                    grandparent->_col = RED;
                    parent->_col = BLACK;
                }
                else
                {
   
                    RotateL(parent);
                    RotateR(grandparent);
                    grandparent->_col = RED;
                    cur->_col = BLACK;
                }
                break;
            }
        }
        else//parent == grandparent->_right
        {
   
            Node* uncle = grandparent->_left;

            if (uncle && uncle->_col == RED)
            {
   
                grandparent->_col = RED;
                parent->_col = uncle->_col = BLACK;
                cur = grandparent;
                parent = cur->_parent;
            }
            else
            {
   
                if (cur == parent->_right)
                {
   
                    RotateL(grandparent);
                    parent->_col = BLACK;
                    grandparent->_col = RED;
                }
                else
                {
   
                    RotateR(parent);
                    RotateL(grandparent);
                    cur->_col = BLACK;
                    grandparent->_col = RED;
                }
            }
            break;
        }
    }
    _root->_col = BLACK;
    return make_pair(iterator(newnode),true );
}

set的查找

  • 红黑树的查找类似于,AVL的查找。本质上是一样的。
Node* find(const T& data)
{
   
    keyofT kot;
    Node* cur = _root;
    while (cur)
    {
   
        if (kot(data) > kot(cur->_data))
        {
   
            cur = cur->_right;
        }
        else if (kot(data) < kot(cur->_data))
        {
   
            cur = cur->_left;
        }
        else
        {
   
            return cur;
        }
    }

    return nullptr;
}

set的迭代器

迭代器的概念

迭代器是一种抽象数据类型,它提供了一种方法来遍历容器中的元素,就像指针遍历数组一样。C++中的迭代器分为五种:输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。set的迭代器是双向迭代器,这意味着它们可以向前和向后遍历元素

迭代器的封装

  • set迭代器类似于list的迭代器
  • 主要区别在于++--
  • Ptr是T*,Ref是T&,设置这么多的模板参数是为适配出const的迭代器
  • 注意的是需要Iterator的构造函数,因为set的迭代器都是const的迭代器。
template<class T,class Ptr,class Ref>
struct TreeIterator
{
   
    typedef RBTreeNode<T> Node;
    typedef TreeIterator<T,Ptr,Ref> self;
    typedef TreeIterator<T, T*, T&> Iterator;
    Node* _node;

    TreeIterator(Node* node)
        :_node(node)
    {
   }
    TreeIterator(const Iterator& it)
        :_node(it._node)
    {
   }

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

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

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

};

迭代器的核心

set的迭代器在内部维护了指向树中节点的指针。由于set是有序的,迭代器在递增或递减时会沿着树的左子树或右子树进行遍历。迭代器的operator++(递增)和operator--(递减)操作会更新迭代器所指向的节点,移动到下一个或上一个有序元素。

前置++

  • 前置++ 的本质上就是中序的遍历,左子树-根-右子树
  • 如果右子树存在就去右子树的最左节点
  • _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)
        {
   
            if (cur == parent->_left)
            {
   
                break;
            }
            else
            {
   
                cur = cur->_parent;
                parent = parent->_parent;
            }

        }

        _node = parent;
    }
    return *this;
}

前置- -

  • 前置-- 是前置++的镜像的一个顺序的访问,右子树-根-左子树
  • 方法是类似于前置++
self& operator--()
{
   
    if (_node->_left)
    {
   
        Node* cur = _node->_right;
        while (cur->_left)
        {
   
            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;
}

迭代器

  • 前面是将迭代器封装,因为正常的++或者--已不可以支持自定义类型的加减
  • 在红黑树的类中实现,在实现set时候只需要调用,在这里面可以认为红黑树是容器的适配器
typedef TreeIterator<T, T*, T&> iterator;

typedef TreeIterator<T,const T*,const T&> const_iterator;

iterator begin()
{
   
    Node* leftmin = _root;
    while (leftmin->_left)
    {
   
        leftmin = leftmin->_left;
    }
    return iterator(leftmin);
}

iterator end()
{
   
    return iterator(nullptr);
}

const_iterator begin()const 
{
   
    Node* leftmin = _root;
    while (leftmin->_left)
    {
   
        leftmin = leftmin->_left;
    }
    return const_iterator(leftmin);
}

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

源码

set

#pragma once
#include"RBTree.h"
#include<iostream>
using namespace std;

namespace Set
{
   
    template<class K>
    class set
    {
   
        struct setofkey
        {
   
            const K& operator()(const K& key)
            {
   
                return key;
            }
        };
    public:

        typedef typename RBTree<K, K, setofkey>::const_iterator iterator;

        typedef typename RBTree<K, K, setofkey>::const_iterator const_iterator;

        iterator begin()const 
        {
   
            return _t.begin();
        }
        iterator end()const 
        {
   
            return _t.end();
        }



        pair<iterator,bool> insert(const K& key)
        {
   
            pair<typename RBTree <K, K, setofkey>::iterator, bool> ret = _t.insert(key);

            return pair<iterator, bool>(ret.first, ret.second);
        }

    private:
        RBTree <K, K, setofkey> _t;
    };
}

红黑树

enum Colour
{
   
    RED,
    BLACK
};
template< class T>
class RBTreeNode
{
   
public:
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    T _data;
    Colour _col;

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

template<class T,class Ptr,class Ref>
struct TreeIterator
{
   
    typedef RBTreeNode<T> Node;
    typedef TreeIterator<T,Ptr,Ref> self;
    typedef TreeIterator<T, T*, T&> Iterator;
    Node* _node;

    TreeIterator(Node* node)
        :_node(node)
    {
   }
    TreeIterator(const Iterator& it)
        :_node(it._node)
    {
   }

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

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

    bool operator != (const self & s) const 
    {
   
        return _node!= s._node;
    }
    bool operator== (const self & s) const 
    {
   
        return _node == s._node;
    }
    self& operator--()
    {
   
        if (_node->_left)
        {
   
            Node* cur = _node->_right;
            while (cur->_left)
            {
   
                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;
    }

    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)
            {
   
                if (cur == parent->_left)
                {
   
                    break;
                }
                else
                {
   
                    cur = cur->_parent;
                    parent = parent->_parent;
                }

            }

            _node = parent;
        }
        return *this;
    }
};

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&> const_iterator;

    iterator begin()
    {
   
        Node* leftmin = _root;
        while (leftmin->_left)
        {
   
            leftmin = leftmin->_left;
        }
        return iterator(leftmin);
    }

    iterator end()
    {
   
        return iterator(nullptr);
    }

    const_iterator begin()const 
    {
   
        Node* leftmin = _root;
        while (leftmin->_left)
        {
   
            leftmin = leftmin->_left;
        }
        return const_iterator(leftmin);
    }

    const_iterator end()const 
    {
   
        return const_iterator(nullptr);
    }
    Node* find(const T& data)
    {
   
        keyofT kot;
        Node* cur = _root;
        while (cur)
        {
   
            if (kot(data) > kot(cur->_data))
            {
   
                cur = cur->_right;
            }
            else if (kot(data) < kot(cur->_data))
            {
   
                cur = cur->_left;
            }
            else
            {
   
                return cur;
            }
        }

        return nullptr;
    }

    pair<iterator,bool> insert(const T& data)
    {
   
        if (_root == nullptr)
        {
   
            _root = new Node(data);
            _root->_col = BLACK;
            return make_pair(iterator(_root),true);
        }
        Node* parent = nullptr;
        Node* cur = _root;

        keyofT kot;

        while (cur)
        {
   
            if (kot(data) > kot(cur->_data))
            {
   
                parent = cur;
                cur = cur->_right;
            }
            else if (kot(data) < kot(cur->_data))
            {
   
                parent = cur;
                cur = cur->_left;
            }
            else
            {
   
                return make_pair(iterator(cur), false);
            }
        }

        cur = new Node(data);
        Node* newnode = cur;
        cur->_col = RED;

        if (kot(data) < kot(parent->_data))
        {
   
            parent->_left = cur;
        }
        else
        {
   
            parent->_right = cur;
        }
        cur->_parent = parent;

        while (parent && parent->_col == RED)
        {
   
            Node* grandparent = parent->_parent;
            if (parent == grandparent->_left)
            {
   
                Node* uncle = grandparent->_right;
                if (uncle && uncle->_col == RED)
                {
   
                    parent->_col = uncle->_col = BLACK;
                    grandparent->_col = RED;
                    cur = grandparent;
                    parent = cur->_parent;
                }
                else//uncle is not or black
                {
   
                    if (cur == parent->_left)
                    {
   
                        RotateR(grandparent);
                        grandparent->_col = RED;
                        parent->_col = BLACK;
                    }
                    else
                    {
   
                        RotateL(parent);
                        RotateR(grandparent);
                        grandparent->_col = RED;
                        cur->_col = BLACK;
                    }
                    break;
                }
            }
            else//parent == grandparent->_right
            {
   
                Node* uncle = grandparent->_left;

                if (uncle && uncle->_col == RED)
                {
   
                    grandparent->_col = RED;
                    parent->_col = uncle->_col = BLACK;
                    cur = grandparent;
                    parent = cur->_parent;
                }
                else
                {
   
                    if (cur == parent->_right)
                    {
   
                        RotateL(grandparent);
                        parent->_col = BLACK;
                        grandparent->_col = RED;
                    }
                    else
                    {
   
                        RotateR(parent);
                        RotateL(grandparent);
                        cur->_col = BLACK;
                        grandparent->_col = RED;
                    }
                }
                break;
            }
        }
        _root->_col = BLACK;
        return make_pair(iterator(newnode),true );
    }

    void RotateR(Node* parent)
    {
   
        Node* cur = parent->_left;
        Node* curright = cur->_right;

        parent->_left = curright;

        if (curright)
        {
   
            curright->_parent = parent;
        }

        cur->_right = parent;

        Node* ppnode = parent->_parent;

        parent->_parent = cur;

        if (ppnode == nullptr)
        {
   
            _root = cur;
            cur->_parent = nullptr;
        }
        else
        {
   
            if (ppnode->_left == parent)
            {
   
                ppnode->_left = cur;
            }
            else
            {
   
                ppnode->_right = cur;
            }

            cur->_parent = ppnode;
        }

    }
    void RotateL(Node* parent)
    {
   

        Node* cur = parent->_right;

        Node* curleft = cur->_left;

        parent->_right = curleft;

        if (curleft)
        {
   
            curleft->_parent = parent;
        }

        cur->_left = parent;

        Node* ppnode = parent->_parent;

        parent->_parent = cur;

        if (parent == _root)
        {
   
            _root = cur;
            cur->_parent = nullptr;
        }
        else
        {
   
            if (ppnode->_left == parent)
            {
   
                ppnode->_left = cur;
            }
            else
            {
   
                ppnode->_right = cur;
            }

            cur->_parent = ppnode;
        }
    }

private:
    Node* _root = nullptr;
};
目录
相关文章
|
21天前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
27 3
【C++】map、set基本用法
|
21天前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
17 5
|
5月前
|
C++ 容器
【C++】map和set封装
【C++】map和set封装
41 2
|
5月前
|
存储 C++ 容器
【C++】map和set深度讲解(下)
【C++】map和set深度讲解(下)
64 2
|
5月前
|
存储 自然语言处理 Java
【C++】map和set深度讲解(上)
【C++】map和set深度讲解(上)
48 2
|
5月前
|
存储 C++ 容器
|
6月前
|
C++ 容器
C++之set/multiset容器
C++之set/multiset容器
|
6月前
|
存储 自然语言处理 C++
【C++航海王:追寻罗杰的编程之路】set|map|multiset|multimap简单介绍
【C++航海王:追寻罗杰的编程之路】set|map|multiset|multimap简单介绍
46 0
【C++航海王:追寻罗杰的编程之路】set|map|multiset|multimap简单介绍
|
6月前
|
存储 算法 NoSQL
C++一分钟之-map与set容器详解
【6月更文挑战第21天】C++ STL的`map`和`set`是基于红黑树的关联容器,提供有序存储和高效查找。`map`存储键值对,键唯一,值可重复;`set`仅存储唯一键。两者操作时间复杂度为O(log n)。常见问题包括键的唯一性和迭代器稳定性。自定义比较函数可用于定制排序规则,内存管理需注意适时释放。理解和善用这些工具能提升代码效率。
69 3
|
6月前
|
存储 编译器 C++