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

1238

被折叠的 条评论
为什么被折叠?



