• set和map的模拟


    目录

    1.介绍

    2.模拟实现

    2.1节点的改造

    2.2迭代器的实现

    2.2.1

    2.2.2

    3.完整代码


    1.介绍

    STL中的set和map的底层都是用红黑树模拟实现的。但set每次只存储一个值,而map存储的是键值对,那是否要写两份红黑树的代码呢?

    可写看下之前写的博客:可怕的红黑树_"派派"的博客-CSDN博客,实现是与map相似的。

    2.模拟实现

    2.1节点的改造

    我们可用模板对红黑树进行改造,让map和set同时适用。

    在红黑树的代码中,我们可把存储每个节点的结构代码体更改为:

    1. enum Colour
    2. {
    3. RED,
    4. BLACK,
    5. };
    6. template<class T>
    7. struct RBTreeNode
    8. {
    9. RBTreeNode* _left;
    10. RBTreeNode* _right;
    11. RBTreeNode* _parent;
    12. T _data; // 存储数据
    13. Colour _col;
    14. RBTreeNode(const T& data)
    15. :_data(data)
    16. , _left(nullptr)
    17. , _right(nullptr)
    18. , _parent(nullptr)
    19. , _col(RED)
    20. {}
    21. };

    其中根据T中的类型进行存储,例如:T可以是set传来的int类型等,map传来的键值对。

    但在插入时需要比较数据的大小,T可能为int类型或则键值对。所以需要再增加一个仿函数,用来提取第一个参数(map和set都是用第一个参数比较大小的)。

    set中:

    1. template<class K>
    2. class Set
    3. {
    4. struct SetKeyOfT
    5. {
    6. const K& operator()(const K& key)
    7. {
    8. return key;
    9. }
    10. };
    11. public
    12. bool insert(const K& key)
    13. {
    14. return _t.Insert(key);
    15. }
    16. private:
    17. RBTree _t;
    18. };

    map中:

    1. template<class K, class V>
    2. class Map
    3. {
    4. struct MapKeyOfT
    5. {
    6. const K& operator()(const pair& kv)//提取第一个参数
    7. {
    8. return kv.first;
    9. }
    10. };
    11. public:
    12. bool insert(const pair& kv)
    13. {
    14. _t.Insert(kv);
    15. }
    16. private:
    17. RBTree, MapKeyOfT> _t;
    18. };

    红黑树的代码部分:

    1. template<class K, class T, class KeyOfT>//keyOfT用来接收仿函数
    2. class RBTree
    3. {
    4. typedef RBTreeNode Node;
    5. public:
    6. bool Insert(const T& data)
    7. {
    8. if (_root == nullptr)
    9. {
    10. _root = new Node(data);
    11. _root->_col = BLACK;
    12. return true;
    13. }
    14. KeyOfT kot;
    15. Node* parent = nullptr;
    16. Node* cur = _root;
    17. while (cur)
    18. {
    19. if (kot(cur->_data) < kot(data))
    20. {
    21. parent = cur;
    22. cur = cur->_right;
    23. }
    24. else if (kot(cur->_data) > kot(data))
    25. {
    26. parent = cur;
    27. cur = cur->_left;
    28. }
    29. else
    30. {
    31. return false;
    32. }
    33. }
    34. cur = new Node(data);
    35. Node* newnode = cur;
    36. cur->_col = RED;
    37. if (kot(parent->_data) < kot(data))
    38. {
    39. parent->_right = cur;
    40. }
    41. else
    42. {
    43. parent->_left = cur;
    44. }
    45. cur->_parent = parent;
    46. ..................
    47. ..................
    48. //旋转.....
    49. }
    50. }

    画图理解下:

     我们可验证下结果是否准确,可在红黑树中定义一个中序遍历代码,然后在set,map中调用。

    1. void test_set1()
    2. {
    3. Set<int> s;
    4. s.insert(8);
    5. s.insert(6);
    6. s.insert(5);
    7. s.insert(7);
    8. s.insert(2);
    9. s.insert(9);
    10. Mapint>m;
    11. m.insert(make_pair("香蕉", 1));
    12. m.insert(make_pair("苹果", 1));
    13. m.insert(make_pair("西瓜", 1));
    14. m.insert(make_pair("草莓", 1));
    15. s.Inorder();
    16. cout << endl;
    17. m.Inorder();
    18. }

     结果:

     

    2.2迭代器的实现

    红黑树也是可以像string,vector一样,通过迭代器像指针一样去遍历节点,不过它更像list的迭代器,不是一个原生指针,而是通过封装了节点的指针,对指针进行操作。

    在红黑树中增加下面的代码:

    1. typedef __RBTreeIterator iterator;
    2. typedef __RBTreeIteratorconst T&, const T*> const_iterator;
    3. iterator Begin()//找其最左节点
    4. {
    5. Node* subLeft = _root;
    6. while (subLeft && subLeft->_left)
    7. {
    8. subLeft = subLeft->_left;
    9. }
    10. return iterator(subLeft);
    11. }
    12. iterator End()
    13. {
    14. return iterator(nullptr);
    15. }
    16. const_iterator Begin() const
    17. {
    18. Node* subLeft = _root;
    19. while (subLeft && subLeft->_left)
    20. {
    21. subLeft = subLeft->_left;
    22. }
    23. return const_iterator(subLeft);
    24. }
    25. const_iterator End() const
    26. {
    27. return const_iterator(nullptr);
    28. }
    29. iterator Find(const K& key)//查找
    30. {
    31. Node* cur = _root;
    32. KeyOfT kot;
    33. while (cur)
    34. {
    35. if (kot(cur->_data) < key)
    36. {
    37. cur = cur->_right;
    38. }
    39. else if (kot(cur->_data) > key)
    40. {
    41. cur = cur->_left;
    42. }
    43. else
    44. {
    45. return iterator(cur);
    46. }
    47. }
    48. return End();
    49. }

    迭代器的实现:

    2.2.1

    1. template<class T, class Ref, class Ptr>
    2. struct __RBTreeIterator
    3. {
    4. typedef RBTreeNode Node;
    5. typedef __RBTreeIterator Self;
    6. Node* _node;
    7. __RBTreeIterator(Node* node)
    8. :_node(node)
    9. {}
    10. Ref operator*()
    11. {
    12. return _node->_data;
    13. }
    14. Ptr operator->()
    15. {
    16. return &_node->_data;
    17. }
    18. Self& operator++()
    19. {
    20. ............
    21. ............
    22. }
    23. Self operator++(int)
    24. {
    25. Self tmp(*this);
    26. ++(*this);
    27. return tmp;
    28. }
    29. Self& operator--()
    30. {
    31. ..........
    32. ..........
    33. }
    34. Self operator--(int)
    35. {
    36. Self tmp(*this);
    37. --(*this);
    38. return tmp;
    39. }
    40. bool operator!=(const Self& s) const
    41. {
    42. return _node != s._node;
    43. }
    44. bool operator==(const Self& s) const
    45. {
    46. return _node == s->_node;
    47. }
    48. };

    2.2.2

    ++的实现:

    ++的实现与中序遍历的规则类似,但每次只走一步。

    思路:当一个节点有右子树时,对其节点++,之后指针是指向该右子树的最左节点。

               当一个节点无右子树时,对其节点++,之后指针是指向(在其祖先里,孩子是属于祖先左              孩子的)

    如图:

    1. Self& operator++()
    2. {
    3. if (_node->_right == nullptr)
    4. {
    5. // 找祖先里面,孩子是父亲左的那个
    6. Node* cur = _node;
    7. Node* parent = cur->_parent;
    8. while (parent && parent->_right == cur)
    9. {
    10. cur = cur->_parent;
    11. parent = parent->_parent;
    12. }
    13. _node = parent;
    14. }
    15. else
    16. {
    17. // 右子树的最左节点
    18. Node* subLeft = _node->_right;
    19. while (subLeft->_left)
    20. {
    21. subLeft = subLeft->_left;
    22. }
    23. _node = subLeft;
    24. }
    25. return *this;
    26. }

    --的实现:

    思路:当左子树不为空时,对其节点--,指向的是该左子树的最右节点。

               当左子树为空时,对其节点--,之后指针是指向(在其祖先里,孩子是属于祖先右                           孩子的)。

    如图:

    1. Self& operator--()
    2. {
    3. if (_node->_left == nullptr)
    4. {
    5. Node* cur = _node;
    6. Node* parent = cur->_parent;
    7. while (parent && cur == parent->_left)
    8. {
    9. cur = cur->_parent;
    10. parent = parent->_parent;
    11. }
    12. _node = parent;
    13. }
    14. else
    15. {
    16. // 左子树的最右节点
    17. Node* subRight = _node->_left;
    18. while (subRight->_right)
    19. {
    20. subRight = subRight->_right;
    21. }
    22. _node = subRight;
    23. }
    24. return *this;
    25. }

    当迭代器实现后,红黑树的插入代码要进行更改,insert函数返回值是pair,第一个参数是指向当前节点的迭代器,第二个参数是true/false;(插入成功返回true,否则返回false)。

    3.完整代码

    set:

    1. template<class K>
    2. class Set
    3. {
    4. struct SetKeyOfT
    5. {
    6. const K& operator()(const K& key)
    7. {
    8. return key;
    9. }
    10. };
    11. public:
    12. typedef typename RBTree::const_iterator iterator;
    13. typedef typename RBTree::const_iterator const_iterator;
    14. iterator begin() const
    15. {
    16. return _t.Begin();
    17. }
    18. iterator end() const
    19. {
    20. return _t.End();
    21. }
    22. pairbool> insert(const K& key)
    23. {
    24. //pair::iterator, bool> ret =
    25. // _t.Insert(key);
    26. auto ret = _t.Insert(key);
    27. return pairbool>(iterator(ret.first._node), ret.second);
    28. }
    29. iterator find(const K& key)
    30. {
    31. return _t.Find(key);
    32. }
    33. private:
    34. RBTree _t;
    35. };

    map代码:

    1. template<class K, class V>
    2. class Map
    3. {
    4. struct MapKeyOfT
    5. {
    6. const K& operator()(const pair& kv)
    7. {
    8. return kv.first;
    9. }
    10. };
    11. public:
    12. typedef typename RBTree, MapKeyOfT>::iterator iterator;
    13. typedef typename RBTree, MapKeyOfT>::const_iterator const_iterator;
    14. iterator begin()
    15. {
    16. return _t.Begin();
    17. }
    18. iterator end()
    19. {
    20. return _t.End();
    21. }
    22. pairbool> insert(const pair& kv)
    23. {
    24. return _t.Insert(kv);
    25. }
    26. iterator find(const K& key)
    27. {
    28. return _t.Find(key);
    29. }
    30. V& operator[](const K& key)
    31. {
    32. pairbool> ret = insert(make_pair(key, V()));
    33. return ret.first->second;
    34. }
    35. private:
    36. RBTree, MapKeyOfT> _t;
    37. };

  • 相关阅读:
    Linux 搭建NFS
    【k8s】浅谈对kubernetes基本概念
    箭头函数的缺点
    用于远程医疗的无创、无袖带血压测量【翻译】
    SpringBoot项目如何引入外部jar及将外部jar打包到项目发布jar包
    python triangle库将一组闭合点转化为三角网格时网格过密的问题
    AiDocZh.com重磅发布!LlamaIndex中文文档上线啦!
    php反序列化
    python爬虫实战-京东商品数据
    浅谈电力电容器技术的发展及选型
  • 原文地址:https://blog.csdn.net/m0_64397669/article/details/126597312