当前位置: 首页 > ds >正文

map和set的设计以及红黑树的设计

1.map和set的底层是红黑树

2.map和set在STL是容器,在我看来,不过也是封装了平衡二叉搜索树红黑树的适配器

我们先看红黑树的设计,看完后map和set的封装易如反掌

#pragma once
#include<utility>
#include<iostream>
using namespace std;
enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};template<class T, class Ref, class Ptr>//T 确定迭代器指向什么类型节点,reference 引用,ptr  pointer指针
struct __RBTreeIterator //不是struct就不能直接访问迭代器成员_node
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)//set中begin()是红黑树begin()的封装,红黑树是普通红黑树成员,红黑树begin()返回的是普通迭代器,但是set中用Iterator来接收返回值,是红黑树中const_iterator的重命名,所以要加一个函数,用iterator构造出const——iterator,才能正常返回: _node(it._node){}Ref operator*(){return _node->_data;}Ptr operator->()//调用时少一个->,编译器会出手{return &_node->_data;}bool operator!=(Self it){return _node != it._node;}Self& operator++()//中序左根右    迭代器不销毁传引用{if (_node->_right){Node* tmp = _node->_right;while (tmp->_left){tmp = tmp->_left;}_node = tmp;}else{Node* cur = _node,*parent=_node->_parent;while (parent && parent->_right == cur)//cur在右边cur遍历根早遍历过了  如果parent为空说明是最右端节点,++置空{cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator--()//--就是++反过来的顺序,右根左{if (_node->_left){Node* tmp = _node->_left;while (tmp->_right){tmp = tmp->right;}_node = tmp;return *this;}else{Node* cur = _node, * parent = _node->_parent;while (parent && parent->_left == cur)//右根左 cur在左边cur遍历根早遍历过了  parent为空则是最左端节点{cur = cur->_parent;parent = parent->_parent;}_node = parent;return *this;}}};template<class K,class T,class KeyOfT>//KeyOfT是仿函数,用来控制节点的比较,因为node里是T,没有K
class RBTree
{typedef RBTreeNode<T>  Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}typedef __RBTreeIterator<T, T&, T*> iterator;typedef __RBTreeIterator<T, const T&, const T*> const_iterator;iterator begin()//传普通红黑树的this指针{Node* tmp = _root;while (tmp && tmp->_left){tmp = tmp->_left;}return tmp;//tmp构造一个const临时iterator,再赋值}iterator end(){return nullptr;}const_iterator begin()const//传const红黑树的this指针,返回const红黑树迭代器{Node* tmp = _root;while (tmp && tmp->_left){tmp = tmp->_left;}return tmp;}const_iterator end()const{return nullptr;}Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) > key){cur = cur->_left;}else if (kot(cur->_data) < key){cur = cur->_right;}else{return cur;}}return nullptr;}pair<iterator, bool> Insert(const T& data){if (_root == nullptr){_root=new Node(data);//BUG--没有把节点变黑_root->_col = BLACK;return make_pair(_root, true);}Node* cur = _root,*parent=nullptr;KeyOfT kot;while (cur){if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else{return make_pair(iterator(cur), false);}}cur = new Node(data);if (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}Node* newnode = cur;cur->_parent = parent;while (parent && parent->_col==RED)//插入的是红色,如果parent为黑就不用调整,为红就调整{//因为parent是红色,所以parent不是根,一定有爷爷节点且为黑色Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col=BLACK;grandfather->_col = RED;//爷爷节点的父节点可能是红,还得向上调整cur = grandfather;parent = cur->_parent;}else//叔叔不存在或者是黑{if (cur == parent->_left)//cur的位置不同旋转就不同{rotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{rotateL(parent);rotateR(grandfather);grandfather->_col = RED;cur->_col = BLACK;}break;//新子树根节点是黑色,无需再向上调}}else//爷爷的右边是parent{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//叔叔为红时调整不需要考虑cur的位置{uncle->_col =parent->_col= BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){rotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{rotateR(parent);rotateL(grandfather);grandfather->_col = RED;cur->_col = BLACK;}break; // 新子树根节点是黑色,无需再向上调}}}//调整完可能有根节点为红_root->_col = BLACK;return make_pair(newnode, true);}bool isRBTree(){if (_root == nullptr) return true;if (_root->_col == RED){cout << "根节点是红" << endl;return false;}int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)benchmark++;cur = cur->_left;}return _Check(_root, 0, benchmark);}
private:bool _Check(Node* root, int blacknum, int benchmark){if (root == nullptr)//root为空说明一条路径结束{if (benchmark == blacknum){return true;}else{cout << "路经黑色节点数量不一" << endl;return false;}}if (root->_col == BLACK) blacknum++;if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "有连续红色节点" << endl;return false;}return _Check(root->_left, blacknum, benchmark) &&_Check(root->_right, blacknum, benchmark);}void _Destroy(Node* root){if (root == nullptr)return;_Destroy(root->_left);_Destroy(root->_right);delete root;}void rotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppnode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void rotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}Node* _root = nullptr;
};

1.红黑树结点的设计

三个指针一个T类型数据,还有一个颜色

2.红黑树的模板参数

第一个是节点排序所用的类型K,第二个是节点里数据的类型T,第三个是把T变成K的仿函数类型

3.g++的红黑树设计思路是一个哨兵节点,一个节点数量

g++中哨兵节点(_M_header)的结构

  • _M_header->_M_parent:指向红黑树的 根节点(如果树非空)。

  • _M_header->_M_left:指向树的 最左节点(即中序遍历的第一个节点)。

  • _M_header->_M_right:指向树的 最右节点(即中序遍历的最后一个节点)。

template <typename _Key, typename _Val, typename _KeyOfValue, typename _Compare, typename _Alloc>
class _Rb_tree {
private:_Rb_tree_node<_Val>* _M_header;  // 哨兵节点(根节点的父节点)size_t _M_node_count;            // 节点数量_Compare _M_key_compare;         // 比较函数(默认 std::less<_Key>)public:// 插入、删除、查找等操作...
};

但是我上面代码的实现并没有设计哨兵节点,也没有节点个数,就一个根节点

4.红黑树的迭代器设计

红黑树的迭代器和list一样是封装了结点指针的类,第一个模板参数是为了形成结点指针类型,二、三模板参数则是为了实例化出迭代器和const迭代器类

5.g++设计迭代器 和  我上面代码设计的迭代器

g++迭代器的 begin() 和 end() 逻辑

  • begin() 直接返回 _M_header->_M_left(最左节点)。

  • end() 返回 哨兵节点_M_header

自己设计的迭代器的 begin() 和 end() 逻辑:

  • begin() 遍历查找返回最左节点。

  • end() 返回 nullptr。

map和set适配红黑树

#pragma once
#include"RBTree.h"
namespace aqc
{template<class K,class V>class map{public:struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;//如果不加typename可能编译器会识别为类中的静态变量iterator begin(){return t.begin();}iterator end(){return t.end();}pair<iterator,bool> Insert(const pair<const K, V>& kv){return t.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = t.Insert(make_pair(key, V()));return ret.first->second;//iterator->返回的是_data的地址}private:RBTree<K, pair<const K,V>, MapKeyOfT> t;};
}#pragma once
#include"RBTree.h"
namespace aqc
{template<class K>class set{public:struct SetKeyOfT{const K& operator()(const K& key)//如果写成struct外面调不到运算符重载{return key;}};typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;//set中二叉树节点的T即为key,不能被修改typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin(){return t.begin();//普通红黑树成员调begin返普通迭代器,接受类型为:const_iterator,因此先前在红黑树加了个const迭代器构造函数}iterator end(){return t.end();}const_iterator begin()const{return t.begin();//const set调用begin:首先普通红黑树调用begin返回普通迭代器,然后接受类型构造}const_iterator end()const{return t.end();}pair<iterator,bool> Insert(const K& key){return t.Insert(key);}private:RBTree<K,K, SetKeyOfT> t;};
}

1.封装map,节点来排序的类型是K,节点里数据的类型pair<const K,V>,还有把T变成K的仿函数类

2.封装set,节点用来排序的类型是K,节点里数据的类型还是K,还有把T变成K的仿函数类

http://www.xdnf.cn/news/4234.html

相关文章:

  • 英伟达语音识别模型论文速读:Fast Conformer
  • 学习黑客Nmap 实战
  • Java学习手册:Spring 多数据源配置与管理
  • 信息系统项目管理工程师备考计算类真题讲解十二
  • 破局者手册 Ⅰ:测试开发核心基础,解锁未来测试密钥!
  • 【NLP】27. 语言模型训练以及模型选择:从预训练到下游任务
  • RAG知识库只是表面简单!
  • Kubernetes排错(七)-节点排错
  • 除了java.nio.file.StandardCopyOption,还有哪些类可以实现文件的复制和移动?
  • C++动态库和静态库的生成和使用
  • linux crash工具详解
  • android-ndk开发(1): 搭建环境
  • 星途-(4)
  • 关于Python:9. 深入理解Python运行机制
  • DeepSeek技术发展详细时间轴与技术核心解析
  • ARM子程序调用与返回
  • vscode运行python的快捷键
  • VirtualBox调整虚拟机内存和CPU
  • 信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(八)
  • 智能体四项关键技术:MCP、A2A、ANP与函数调用的深度解析
  • 判断字符是否唯一 --- 位运算
  • 《冰雪三职业》:战士玩法攻略!
  • 精益数据分析(39/126):SaaS与移动应用商业模式的关键要点剖析
  • P6822 [PA 2012 Finals] Tax 题解
  • 【项目】基于ArkTS的网吧会员应用开发(2)
  • Qt天气预报系统更新UI界面
  • ansible基础-优化
  • 代码随想录算法训练营day9:字符串part02
  • 英伟达开源英语自动语音识别模型:nvidia/parakeet-tdt-0.6b-v2
  • android zxing QrCode 库集成转竖屏适配问题