• set和map的封装


    目录

    介绍

    红黑树代码 

    set

    insert的迭代器转换问题

    为什么会有这样的问题?

    如何解决

    代码 

    map

    注意点

    代码


    介绍

    set和map的底层都是红黑树,所以我们可以在自己实现的红黑树(简易版)的基础上,进行封装,成为简易的set和map

    红黑树代码 

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. // 有迭代器的红黑树
    10. namespace my_RB_Tree
    11. {
    12. enum colour
    13. {
    14. black,
    15. red
    16. };
    17. template <class T>
    18. struct RBTreeNode // 结点
    19. {
    20. RBTreeNode(const T& data)
    21. : _left(nullptr),
    22. _right(nullptr),
    23. _parent(nullptr),
    24. _col(red),
    25. _data(data)
    26. {
    27. }
    28. RBTreeNode* _left;
    29. RBTreeNode* _right;
    30. RBTreeNode* _parent;
    31. colour _col;
    32. T _data;
    33. };
    34. template <class T, class Ptr, class Ref> // T是元素类型,ptr是指针类型,ref是引用类型(后两种会有const类型)
    35. struct RBTreeIterator // 迭代器
    36. {
    37. typedef RBTreeNode Node;
    38. typedef RBTreeIterator Self;
    39. //为了可以能让普通迭代器初始化const迭代器,需要来一个普通迭代器对象
    40. typedef RBTreeIterator iterator;
    41. Node* _pNode;
    42. RBTreeIterator(Node* pNode)
    43. : _pNode(pNode)
    44. {
    45. }
    46. RBTreeIterator(const iterator& it) // const迭代器时,它是一个初始化;普通迭代器时,它是一个拷贝
    47. : _pNode(it._pNode)
    48. {
    49. }
    50. // 让迭代器具有类似指针的行为
    51. Ref operator*()
    52. {
    53. return _pNode->_data;
    54. }
    55. Ptr operator->()
    56. {
    57. return &(_pNode->_data);
    58. }
    59. // 让迭代器可以移动:前置/后置++
    60. Self& operator++()
    61. {
    62. Increament();
    63. return *this;
    64. }
    65. Self operator++(int)
    66. {
    67. Self tmp(*this);
    68. Increament();
    69. return tmp;
    70. }
    71. // 让迭代器可以移动:前置/后置--
    72. Self& operator--()
    73. {
    74. DeIncreament();
    75. return *this;
    76. }
    77. Self operator--(int)
    78. {
    79. Self tmp(*this);
    80. DeIncreament();
    81. return tmp;
    82. }
    83. // 让迭代器可以比较
    84. bool operator!=(const Self& s) const
    85. {
    86. return _pNode != s._pNode;
    87. }
    88. bool operator==(const Self& s) const
    89. {
    90. return _pNode == s._pNode;
    91. }
    92. private:
    93. void Increament();
    94. void DeIncreament();
    95. };
    96. // 为了后序封装map和set,本代码的红黑树会有一个作为哨兵位的头结点
    97. template <class K, class T, class KeyOfT> // K是关键字的类型,T是元素类型(区分这两个的原因:会用该红黑树封装成set和map,而map是key_value的)
    98. // keyofT是返回关键字类型的值(否则map无法返回)
    99. class RBTree // 红黑树
    100. {
    101. public:
    102. typedef RBTreeNode Node;
    103. typedef RBTreeIterator iterator;
    104. typedef RBTreeIteratorconst T*, const T&> const_iterator;
    105. public:
    106. RBTree()
    107. {
    108. _pHead = new Node(T());
    109. _pHead->_left = _pHead;
    110. _pHead->_parent = nullptr;
    111. _pHead->_right = _pHead;
    112. }
    113. // 在红黑树中插入值为data的节点,插入成功返回true,否则返回false
    114. std::pairbool> Insert(const T& data);
    115. // 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
    116. Node* Find(const K& data);
    117. // 获取红黑树最左侧节点
    118. Node* LeftMost() const;
    119. // 获取红黑树最右侧节点
    120. Node* RightMost() const;
    121. iterator begin()
    122. {
    123. return iterator(LeftMost());
    124. }
    125. iterator end()
    126. {
    127. return iterator(_pHead);
    128. }
    129. const_iterator begin() const
    130. {
    131. return const_iterator(LeftMost());
    132. }
    133. const_iterator end() const
    134. {
    135. return const_iterator(_pHead);
    136. }
    137. // 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
    138. bool IsValidRBTRee()
    139. {
    140. Node* root = _pHead->_parent;
    141. if (root->_col == red)
    142. {
    143. return false;
    144. }
    145. int count = 0;
    146. find_blacknode(count, _pHead->_parent);
    147. return _IsValidRBTRee(_pHead->_parent, count, 0);
    148. }
    149. private:
    150. bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack);
    151. // 左单旋
    152. void RotateL(Node* pParent);
    153. // 右单旋
    154. void RotateR(Node* pParent);
    155. // 为了操作树简单起见:获取根节点
    156. Node*& GetRoot()
    157. {
    158. return _pHead->_parent;
    159. }
    160. void find_blacknode(int& count, Node* root)
    161. {
    162. if (root == nullptr)
    163. {
    164. return;
    165. }
    166. if (root->_col == black)
    167. {
    168. ++count;
    169. }
    170. find_blacknode(count, root->_left);
    171. find_blacknode(count, root->_right);
    172. }
    173. private:
    174. Node* _pHead = nullptr;
    175. };
    176. template <class K, class T, class KeyOfT>
    177. void RBTree::RotateL(Node* pParent)
    178. {
    179. Node* cur = pParent->_right, * curleft = cur->_left;
    180. // 连接p和cur左树,因为该位置被p占据
    181. pParent->_right = curleft;
    182. if (curleft)
    183. {
    184. curleft->_parent = pParent;
    185. }
    186. // 连接父结点
    187. if (pParent->_parent != _pHead)
    188. {
    189. Node* ppnode = pParent->_parent;
    190. if (ppnode->_left == pParent)
    191. {
    192. ppnode->_left = cur;
    193. }
    194. else
    195. {
    196. ppnode->_right = cur;
    197. }
    198. cur->_parent = ppnode;
    199. }
    200. else
    201. {
    202. _pHead->_parent = cur;
    203. cur->_parent = _pHead;
    204. }
    205. // 连接p和cur
    206. pParent->_parent = cur;
    207. cur->_left = pParent;
    208. }
    209. template <class K, class T, class KeyOfT>
    210. void RBTree::RotateR(Node* pParent)
    211. {
    212. Node* cur = pParent->_left, * curright = cur->_right;
    213. // 连接p和cur右树,因为该位置被p占据
    214. pParent->_left = curright;
    215. if (curright)
    216. {
    217. curright->_parent = pParent;
    218. }
    219. // 连接父结点
    220. if (pParent->_parent != _pHead)
    221. {
    222. Node* ppnode = pParent->_parent;
    223. if (ppnode->_left == pParent)
    224. {
    225. ppnode->_left = cur;
    226. }
    227. else
    228. {
    229. ppnode->_right = cur;
    230. }
    231. cur->_parent = ppnode;
    232. }
    233. else
    234. {
    235. _pHead->_parent = cur;
    236. cur->_parent = _pHead;
    237. }
    238. // 连接p和cur
    239. pParent->_parent = cur;
    240. cur->_right = pParent;
    241. }
    242. template <class K, class T, class KeyOfT>
    243. typename RBTree::Node* RBTree::LeftMost() const
    244. {
    245. Node* cur = _pHead->_parent;
    246. while (cur->_left)
    247. {
    248. cur = cur->_left;
    249. }
    250. return cur;
    251. }
    252. template <class K, class T, class KeyOfT>
    253. typename RBTree::Node* RBTree::RightMost() const
    254. {
    255. Node* cur = _pHead->_parent;
    256. while (cur->_right)
    257. {
    258. cur = cur->_right;
    259. }
    260. return cur;
    261. }
    262. template <class K, class T, class KeyOfT>
    263. typename RBTree::Node* RBTree::Find(const K& data) // 注意这里,
    264. {
    265. Node* cur = _pHead->_parent;
    266. KeyOfT kot;
    267. while (cur)
    268. {
    269. if (data > kot(cur->_data))
    270. {
    271. cur = cur->_right;
    272. }
    273. else if (data < kot(cur->_data))
    274. {
    275. cur = cur->_left;
    276. }
    277. else
    278. {
    279. return cur;
    280. }
    281. }
    282. return nullptr;
    283. }
    284. template <class K, class T, class KeyOfT>
    285. std::pair<typename RBTree::iterator, bool> RBTree::Insert(const T& data) // 为了和map适配,要返回pair类型
    286. //(first是插入元素所在的迭代器,second是bool值,判断是否成功插入)
    287. {
    288. KeyOfT kot;
    289. Node* newnode = nullptr;
    290. if (_pHead->_parent == nullptr)
    291. {
    292. newnode = new Node(data);
    293. newnode->_col = black;
    294. _pHead->_parent = newnode;
    295. newnode->_parent = _pHead;
    296. return std::make_pair(iterator(newnode), true);
    297. }
    298. else
    299. {
    300. Node* cur = _pHead->_parent, * parent = cur;
    301. while (cur)
    302. {
    303. if (kot(data) > kot(cur->_data))
    304. {
    305. parent = cur;
    306. cur = cur->_right;
    307. }
    308. else if (kot(data) < kot(cur->_data))
    309. {
    310. parent = cur;
    311. cur = cur->_left;
    312. }
    313. else
    314. {
    315. return std::make_pair((iterator)cur, false);
    316. }
    317. }
    318. newnode = new Node(data);
    319. cur = newnode;
    320. cur->_parent = parent;
    321. if (kot(parent->_data) > kot(cur->_data))
    322. {
    323. parent->_left = cur;
    324. }
    325. else
    326. {
    327. parent->_right = cur;
    328. }
    329. Node* grandfather = nullptr;
    330. while (parent != _pHead && parent->_col == red)
    331. {
    332. grandfather = parent->_parent; // 因为父结点是红色,所以肯定有爷爷结点(注意红黑树规则:根结点必须是黑色)
    333. if (grandfather->_left == parent) // 确定父亲位置
    334. {
    335. Node* uncle = grandfather->_right; // 也就能确定叔叔位置
    336. if (uncle && uncle->_col == red)
    337. {
    338. parent->_col = uncle->_col = black;
    339. grandfather->_col = red;
    340. }
    341. else // 如果uncle不存在/为黑,就需要旋转+变色了
    342. {
    343. // 需要先判断旋转类型(也就是判断 -- parent和cur的相对位置)
    344. if (parent->_left == cur)
    345. {
    346. // 一条偏右的直线,需要右旋
    347. RotateR(grandfather);
    348. // 旋转完后parent成为根结点
    349. // 更改完结点指向后,就可以改颜色了(都是根结点为黑,另外两个为红)
    350. parent->_col = black;
    351. cur->_col = grandfather->_col = red; // 和cur一层
    352. }
    353. else
    354. {
    355. // 拐角在左边,也就是先左旋,再右旋
    356. RotateL(parent);
    357. RotateR(grandfather);
    358. // cur成为根结点
    359. // 改颜色
    360. cur->_col = black;
    361. parent->_col = grandfather->_col = red;
    362. }
    363. break;
    364. }
    365. }
    366. else // parent在grandfather的右树
    367. {
    368. Node* uncle = grandfather->_left;
    369. if (uncle && uncle->_col == red)
    370. {
    371. parent->_col = uncle->_col = black;
    372. grandfather->_col = red;
    373. }
    374. else // 如果uncle不存在/为黑,就需要旋转+变色了
    375. {
    376. // 需要先判断旋转类型(也就是判断 -- parent和cur的相对位置)
    377. if (parent->_right == cur)
    378. {
    379. // 一条偏左的直线,需要左旋
    380. RotateL(grandfather);
    381. parent->_col = black;
    382. cur->_col = grandfather->_col = red; // 和cur一层
    383. }
    384. else
    385. {
    386. // 拐角在右边,也就是先右旋,再左旋
    387. RotateR(parent);
    388. RotateL(grandfather);
    389. // 改颜色
    390. cur->_col = black;
    391. parent->_col = grandfather->_col = red;
    392. }
    393. break;
    394. }
    395. }
    396. cur = grandfather; // 注意,这里会改cur的指向,但返回值需要返回插入位置的迭代器,所以需要另外保存
    397. parent = cur->_parent;
    398. }
    399. (_pHead->_parent)->_col = black; // 根结点必须为黑(防止它在上面的循环中被修改)
    400. }
    401. _pHead->_left = LeftMost();
    402. _pHead->_right = RightMost();
    403. //std::cout << (_pHead->_left)->_data << " " << (_pHead->_right)->_data << std::endl;
    404. return std::make_pair(iterator(newnode), true);
    405. }
    406. template <class K, class T, class KeyOfT>
    407. bool RBTree::_IsValidRBTRee(Node* cur, size_t blackCount, size_t pathBlack)
    408. {
    409. if (cur == nullptr)
    410. {
    411. // 到空结点后,就说明一条路径已经走通了,可以用得到的黑色结点数与基准数对比,不一样就说明红黑树错误
    412. if (pathBlack != blackCount)
    413. {
    414. return false;
    415. }
    416. else
    417. {
    418. return true;
    419. }
    420. }
    421. if (cur->_parent)
    422. {
    423. Node* ppnode = cur->_parent;
    424. if (cur->_col == red && ppnode->_col == red)
    425. {
    426. return false;
    427. }
    428. }
    429. if (cur->_col == black)
    430. {
    431. ++pathBlack;
    432. }
    433. return _IsValidRBTRee(cur->_left, blackCount, pathBlack) && _IsValidRBTRee(cur->_right, blackCount, pathBlack);
    434. }
    435. template <class T, class Ptr, class Ref>
    436. void RBTreeIterator::Increament()
    437. {
    438. Node* cur = _pNode, * parent = _pNode->_parent;
    439. if (cur->_right)
    440. {
    441. // 找到右子树的最小结点
    442. Node* curright = cur->_right;
    443. while (curright->_left)
    444. {
    445. curright = curright->_left;
    446. }
    447. _pNode = curright;
    448. }
    449. else
    450. {
    451. while (parent->_parent != cur && parent->_right == cur) // 找到cur是parent的左结点的位置,这样parent的位置就是下一个位置
    452. {
    453. cur = parent;
    454. parent = parent->_parent;
    455. }
    456. _pNode = parent;
    457. }
    458. }
    459. template <class T, class Ptr, class Ref>
    460. void RBTreeIterator::DeIncreament()
    461. {
    462. Node* cur = _pNode, * parent = _pNode->_parent;
    463. if (cur->_left)
    464. {
    465. // 找到左子树的最大结点
    466. Node* curleft = cur->_left;
    467. while (curleft->_right)
    468. {
    469. curleft = curleft->_right;
    470. }
    471. _pNode = curleft;
    472. }
    473. else
    474. {
    475. while (parent->_parent != cur && parent->_left == cur) // 找到cur是parent的左结点的位置,这样parent的位置就是下一个位置
    476. {
    477. cur = parent;
    478. parent = parent->_parent;
    479. }
    480. _pNode = parent;
    481. }
    482. }
    483. }

    set

    set我们只实现它的插入和迭代器部分,大概可以看到效果就行

    insert的迭代器转换问题

    不考虑别的,因为insert返回的都是pair类型的,都是迭代器+布尔值,所以set直接调用红黑树的插入即可

    但是,编译过不去!

    大概就是说,普通迭代器无法转换为const迭代器

    为什么会有这样的问题?

    注意,set中,无论是普通迭代器还是const迭代器,其实都封装的是红黑树的const迭代器

    stl源码中就是这么定义的:

    • 但是,tree的insert返回的是普通迭代器,而set的insert要返回的是const迭代器
    • 这就存在一个普通迭代器向const迭代器转换的过程

    如何解决

    所以我们需要在红黑树的迭代器类中增加这一功能

    1. typedef RBTreeNode Node;
    2. typedef RBTreeIterator Self;
    3. //为了可以能让普通迭代器初始化const迭代器,需要来一个普通迭代器对象
    4. typedef RBTreeIterator iterator;
    5. Node* _pNode;
    6. RBTreeIterator(Node* pNode)
    7. : _pNode(pNode)
    8. {}
    9. RBTreeIterator(const iterator& it) // const迭代器时,它是一个初始化;普通迭代器时,它是一个拷贝
    10. : _pNode(it._pNode)
    11. {}

    代码 

    1. #include "RB_Tree.hpp"
    2. namespace my_set
    3. {
    4. template <class K>
    5. class set
    6. {
    7. struct SetKeyOfT
    8. {
    9. const K& operator()(const K& key)
    10. {
    11. return key;
    12. }
    13. };
    14. public:
    15. typedef typename my_RB_Tree::RBTree::const_iterator iterator;
    16. typedef typename my_RB_Tree::RBTree::const_iterator const_iterator;
    17. const_iterator begin() const
    18. {
    19. return _t.begin();
    20. }
    21. const_iterator end() const
    22. {
    23. return _t.end();
    24. }
    25. std::pairbool> insert(const K& data) {
    26. //return _t.Insert(data);
    27. //这里在构建时,set的insert调用tree的insert
    28. //而tree中insert的返回值,返回的pair中,第一个成员是tree的普通迭代器
    29. //然后回到该函数,该函数返回的pair的第一个成员是set中的普通迭代器(实质上是tree中的const迭代器)
    30. //所以我们本质上是用不同类型的pair在赋值
    31. //所以要先转换
    32. std::pair<typename my_RB_Tree::RBTree::iterator, bool> ret = _t.Insert(data); //这里是tree的普通迭代器
    33. iterator it(ret.first);
    34. return std::pairbool>(it,ret.second); //这里是要用普通迭代器初始化一个const迭代器,所以需要在tree迭代器中增加这个功能
    35. }
    36. private:
    37. my_RB_Tree::RBTree _t;
    38. };
    39. }

    map

    注意点

    map的重点就在insert和[ ]的重载上

    也没啥别的了,就需要自己先构建一个pair类型,其他的就注意返回值和接收值到底是谁

    K:key值类型    V:value类型     T:map的元素类型

    代码

    1. #include "RB_Tree.hpp"
    2. namespace my_map
    3. {
    4. template <class K, class V>
    5. class map
    6. {
    7. public:
    8. typedef std::pair<const K, V> T; // map中key不能变,value可以变
    9. struct MapKeyOfT
    10. {
    11. const V &operator()(const T &data)
    12. {
    13. return data.second;
    14. }
    15. };
    16. typedef typename my_RB_Tree::RBTree::iterator iterator;
    17. typedef typename my_RB_Tree::RBTree::const_iterator const_iterator;
    18. iterator begin()
    19. {
    20. return _t.begin();
    21. }
    22. iterator end()
    23. {
    24. return _t.end();
    25. }
    26. const_iterator begin() const
    27. {
    28. return _t.begin();
    29. }
    30. const_iterator end() const
    31. {
    32. return _t.end();
    33. }
    34. std::pairbool> insert(const T &data)
    35. {
    36. return _t.Insert(data);
    37. }
    38. V &operator[](const K &data)
    39. {
    40. auto ret = insert(std::make_pair(data,V()));
    41. return (ret.first)->second;
    42. }
    43. private:
    44. my_RB_Tree::RBTree _t;
    45. };
    46. }

  • 相关阅读:
    git push出现git@github.com: Permission denied (publickey) 解决办法
    计算机视觉CV
    Android——Gradle插件项目根目录settings.gradle和build.gradle
    SpringBoot-34-shiro整合thymeleaf
    腾讯云短信服务实现 Java 发送手机验证码(SpringBoot+Redis 实现)
    机器学习实战(11)——初识人工神经网络
    巧用 “火焰图” 快速分析链路性能
    mysql 查找表中重复数据 类型题目
    pycharm/vscode 配置black和isort
    2022年最新山西建筑施工架子工(建筑特种作业)模拟考试试题及答案
  • 原文地址:https://blog.csdn.net/m0_74206992/article/details/133517111