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的仿函数类