上篇文章介绍了红黑树,分析了其规则,和插入后如果导致不平衡该如何进行调整,大致理解了红黑树的底层原理,本篇文章就来介绍一下如果用红黑树封装出map和set。
封装map
主要的函数(erase这里不实现,后期也许会加上)
map是用来存储键值对的,可以通过key映射一个值val,每个key只可以对应一个val是一对一的(如果想要实现一个key可以对应多个不同的val,就可以使用multimap),对于放到map中的键值对是按照键值对的一个参数key来搜索的,需要注意的是因为map符合二叉搜索树的原则,那么其输出的时候就是默认有序的(中序遍历),而后边讲的哈希表实现的Unordered_map因为其底层结构是通过散列表映射,利用链表解决不同的key映射冲突实现的,所以其遍历出来是无序的。
- 首先在这里贴上上篇文章的红黑树的为了封装而该进的代码,主要是改变了insert函数的返回值类型,与库里面的一致,同时实现了operator[]的运算符重载,实现了按下标的直接访问。
enum color//颜色枚举
{
RED,
BLACK,
};
template<class T>
struct RBTreeNode//封装树的结点
{
//三叉链
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
color _col;
T _data;//存放的数据,对于set来说这里的T是K,对于map来说这里的T是一个键值对pair<K,V> 后面会详解
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED)
, _data(data)
{
}
};
//迭代器的封装
//Ref Ptr分别代表这返回的引用和指针,通过这两个模板参数可以实现对const对象调用对应函数可以返回const引用和const指针
//对于非const对象返回非const引用和非const指针
template<class T,class Ref,class Ptr>
struct __RBTree_Iterator
{
//解释:这里的迭代器其实就是对一个结点指针的封装,如果想让迭代器指向位置改变就是对器成员变量的_root的改变(当然这里是根据结构来实现的指针指向的改变,上篇文章有介绍其原理);如果想的得到对应的迭代器的数据就是访问该成员变量_root这个结点指针指向的数据即可(*的重载),对应箭头的重载就是再*重载的基础上返回其地址即可;后面的判断相等不等就是判断两个迭代器的成员变量_root是否相等即可。
typedef __RBTree_Iterator<T, Ref, Ptr> self;//给个类型重定义,简化复杂类型
typedef RBTreeNode<T> Node;
__RBTree_Iterator(Node* root)
:_root(root)
{
}
Ref operator*()//这里是无参的返回的额是当前迭代器对象的数据
{
return _root->_data;
}
Ptr operator->()
{
return &(_root->_data);
}
self& operator=(const self& it)
{
_root = it._root;
}
self& operator++()
{
if (_root->_right == nullptr)
{
Node* cur = _root;
Node* parent = _root->_parent;
while (parent&&parent->_left != cur)//要保证最后一个结点的
{
cur = cur->_parent;
parent = parent->_parent;
}
_root = parent;
}
else
{
Node* right = _root->_right;
while (right->_left)
{
right = right->_left;
}
_root = right;
}
return *this;
}
self& operator++(int)
{
//self tmp = *this;
self tmp(*this);
++ *this;
return tmp;
}
self& operator--()
{
if (_root->_left==nullptr)
{
Node* cur = _root;
Node* parent = _root->_parent;
while (parent&&parent->_right != cur)
{
cur = cur->_parent;
parent = parent->_parent;
}
_root = parent;
}
else
{
Node* left = _root->_left;
while (left->_right)
{
left = left->_right;
}
_root = left;
}
return *this;
}
self& operator--(int)
{
//self tmp = *this;
self tmp(*this);
-- *this;
return tmp;
}
bool operator==(const self& it)const
{
return _root == it._root;
}
bool operator!=(const self& it)const
{
return _root != it._root;
}
Node* _root;
};
//红黑树的实现 代码有点长,如果想看懂map的封装是如何实现的,就必须先看懂红黑树的实现,这样才能看清底层原理的,理解更深
template<class K, class T, class keyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;//对结点的类型重定义
public:
typedef __RBTree_Iterator<T, T&, T*> iterator;//对迭代器的类型重定义 简化代码
typedef __RBTree_Iterator<T,const T&,const T*> const_iterator;//constd迭代器
RBTree()
:_root(nullptr)
{}
iterator begin()//返回迭代器开始位置 也就是红黑树的最左边结点封装的迭代器
{
Node* cur = _root;
while (cur&&cur->_left)//找树的最左边结点
{
cur = cur->_left;
}
return iterator(cur);//返回其封装的迭代器即可
}
iterator end()
{
return iterator(nullptr);//返回空指针封装的迭代器
//begin()最后遍历完数后会指向根结点的父亲,也就是空结点封装出来的迭代器,那么只需要让end与其相同即可
}
const_iterator begin()const //const对象的begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return const_iterator(cur);
}
const_iterator end()const //const对象的end()
{
return const_iterator(nullptr);
}
iterator rbegin()//反向迭代器
{
Node* cur = _root;
while (cur && cur->_right)
{
cur = cur->_right;
}
return iterator(cur);
}
iterator rend()
{
return iterator(nullptr);
}
const_iterator rbegin()const
{
Node* cur = _root;
while (cur && cur->_right)
{
cur = cur->_right;
}
return const_iterator(cur);
}
const_iterator rend()const
{
return const_iterator(nullptr);
}
//左旋代码 这个树的旋转代码必须要理解透彻,才能更好的理解红黑树的底层
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 (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subR;
}
else if (ppnode->_right == parent)
{
ppnode->_right = subR;
}
else
{
cout << "内部错误!" << endl;
assert(false);
}
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 if (ppnode->_right == parent)
{
ppnode->_right = subL;
}
else
{
cout << "内部错误!" << endl;
assert(false);
}
subL->_parent = ppnode;
}
}
//插入代码 其返回值变成了一个键值对,第一个参数插入结点的迭代器,第二个参数是一个布尔类型,代表是否插入成功
pair<iterator,bool> insert(const T& kv)
{
keyOfT kot;
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return make_pair(iterator(_root),true);//第一次插入就是返回的根节点的迭代器和true构成的键值对
}
else
{
Node* parent = nullptr;
Node* cur = _root;
//寻找新增结点的插入位置
while (cur)
{
//if (cur->_kv.first < kv.first)
if (kot(cur->_data) < kot(kv))
{
parent = cur;
cur = cur->_right;
}
//else if (cur->_kv.first > kv.first)
else if (kot(cur->_data) > kot(kv))
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(iterator(cur), false);
}
}
//走到这里cur已经为空了 直接将新结点赋值给cur
cur = new Node(kv);
Node* newnode = cur;//记录插入的结点 方便后面cur变化后仍要返回插入结点位置的迭代器!!!
cur->_col = RED;
//链接 新增结点和其父结点
//if (cur->_kv.first < parent->_kv.first)
if (kot(cur->_data) < kot(parent->_data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//调整红黑树 维持规则
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
Node* uncle = nullptr;
//判断父亲结点是祖父结点的左还是右 后面根据这里来判断是需要进行旋转的方向是左还是右
if (grandfather->_left == parent)
{
uncle = grandfather->_right;
}
else
{
uncle = grandfather->_left;
}
if (parent && parent->_col == RED && uncle && uncle->_col == RED)//叔叔存在且为红
{
//父亲和叔叔结点变黑色 祖父变红色(如果是根节点变回黑,在每次循环完都会将根节点置黑)
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;//迭代往上调整
parent = cur->_parent;
}
else if (parent && parent->_col == RED && (uncle == nullptr || (uncle != nullptr && uncle->_col == BLACK)))//叔叔不存在或存在且为黑
{
//左右双旋加变色处理
if (grandfather->_left == parent && cur == parent->_right)//叔叔不存在
{
// g
// p
// c
RotateL(parent);
swap(parent, cur);
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
//右单旋加变色处理
else if (grandfather->_left == parent && cur == parent->_left)
{
// g
// p
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
//右左双旋加变色处理
else if (grandfather->_right == parent && cur == parent->_left)
{
// g
// p
// c
RotateR(parent);
swap(parent, cur);
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
//左单旋加变色处理
else if (grandfather->_right == parent && cur == parent->_right)
{
// g
// p
// c
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
_root->_col = BLACK;//为了防止叔叔存在且为红的情况把根节点变成红色,所以在每次循环结束后将根结点变成黑
return make_pair(iterator(newnode), true);//插入成功就返回插入结点和ture构成的键值对
}
}
//下面的是一些无关紧要的函数,可以不看
size_t getMaxHight()
{
return _getMaxHight(_root);
}
size_t _getMaxHight(Node* root)
{
if (root == nullptr)
{
return 0;
}
size_t LMH = _getMaxHight(root->_left);
size_t RMH = _getMaxHight(root->_right);
return LMH > RMH ? LMH + 1 : RMH + 1;
}
size_t getMinHight()
{
return _getMinHight(_root);
}
size_t _getMinHight(Node* root)
{
if (root == nullptr)
{
return 0;
}
size_t LMH = _getMinHight(root->_left);
size_t RMH = _getMinHight(root->_right);
return LMH < RMH ? LMH + 1 : RMH + 1;
}
bool checkIsRBTree()
{
if (_root == nullptr)//空树也是红黑树
{
return true;
}
if (_root->_col != BLACK)
{
cout << "根结点不为黑!" << endl;
return false;
}
Node* cur = _root;
size_t standardBlackCount = 0;
while (cur)
{
if (cur->_col == BLACK)
{
standardBlackCount++;
}
cur = cur->_left;
}
size_t k = 0;
return _checkIsRBTree(_root, standardBlackCount, k);
}
//传一个最短基准值 判断每条路径是否超过其两倍
bool _checkIsRBTree(Node* root, size_t standardCount, size_t blackCount)
{
if (root == nullptr)
{
if (standardCount != blackCount)
{
cout << "不符合每条路径上的黑结点数目相同!" << endl;
return false;
}
return true;
}
//统计当前路径的黑结点的数量
if (root->_col == BLACK)
blackCount++;
Node* parent = root->_parent;
if (root->_col == RED && parent && parent->_col == RED)
{
cout << "存在连续红结点!" << endl;
return false;
}
return _checkIsRBTree(root->_left, standardCount, blackCount) && _checkIsRBTree(root->_right, standardCount, blackCount);
}
void print()
{
_print(_root);
}
void _print(Node* root)
{
if (root == nullptr)
{
return;
}
_print(root->_left);
cout << root->_data.first << " " << root->_data.second << endl;
_print(root->_right);
}
private:
Node* _root;
};
map的结构(说白了就是个壳子,是用红黑树封装出来的,其成员函数中有颗红黑树,其成员函数都是调用的红黑树的成员函数)
template<class K, class V>//模板参数K V 一个代表键的类型 一个代表值的类型
class map
{
//因为红黑树里的结点存放的数据是T类型,那么对于map来说里面就得是一个键值对,那么映射位置的时候就需要获得Key,就得返回pair里的第一个参数K 所以给map设计一个仿函数来实现这一功能
struct keyOfT
{
const K& operator()(const pair<K, V>& data)
{
return data.first;
}
};
public:
//类型重定义 简化类型 简化代码
typedef typename RBTree<K, pair<K, V>, keyOfT>::iterator iterator;
typedef typename RBTree<K, pair<K, V>, keyOfT>::const_iterator const_iterator;
pair<iterator,bool> insert(const pair<K, V>& data)//调用红黑树的insert即可
{
return _t.insert(data);
}
V& operator[](const K& key)
{
//这里的[]的重载是为了实现对已经存在的键值对根据指定的键实现对其的值的修改
//对不存在的键值对就插入该键,该键对应的值是缺省的!
return insert(make_pair(key,V())).first->second;
}
//自己加的 不是库中的 无关紧要 可忽略
void print()
{
_t.print();
}
//迭代器也是直接调用红黑树的迭代器即可 因为是封装,就是在红黑树的基础上包装
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
iterator rbegin()
{
return _t.rbegin();
}
iterator rend()
{
return _t.rend();
}
private:
//这里是封装的关键,这里的红黑树的第二个参数T我们给的是一个键值对pair<K,V>
RBTree<K, pair<K, V>, keyOfT> _t;//这里的成员变量就是一颗红黑树!!!
};
测试
void testmap1()
{
map<string, string> m;
m.insert(make_pair("cow", "牛"));//测试插入
m["apple"] = "苹果";//测试[]重载的功能
m["computer"] = "电脑";
m["keyboard"] = "键盘";
m["language"] = "语言";
m["left"] = "左边";
for (auto& e : m)
{
cout << e.first<<" "<<e.second << endl;
}
cout << endl;
m["left"] = "左边 剩下";//修改已插入键值对的值
for (auto& e : m)
{
cout << e.first << " " << e.second << endl;
}
cout << endl;
map<string, string>::iterator it = m.begin();//迭代器遍历 测试迭代器
while (it != m.end())
{
cout << it->first << " " << it->second << endl;
++it;
}
}
set封装
template<class K>
class set
{
struct keyOfT
{
const K& operator()(const K& data)
{
return data;
}
};
public:
typedef typename RBTree<K, K, keyOfT>::const_iterator iterator;//set 中的key是不可以改的 无论是const迭代器还是非const迭代器都不能改
typedef typename RBTree<K, K, keyOfT>::const_iterator const_iterator;
pair<iterator,bool> insert(const K& key)
{
pair<typename RBTree<K, K, keyOfT>::iterator, bool> ret = _t.insert(key);
return make_pair(iterator(ret.first._root),ret.second);
}
void print()
{
_t.print();
}
iterator begin()const
{
return _t.begin();
}
iterator end()const
{
return _t.end();
}
iterator rbegin()const
{
return _t.rbegin();
}
iterator rend()const
{
return _t.rend();
}
private:
RBTree<K, K, keyOfT> _t;
};
测试
void testset()
{
set<int> s;
s.insert(1);
s.insert(4);
s.insert(5);
s.insert(2);
s.insert(7);
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << endl;
//++* it;
++it;
}
cout <<"下面是测试反向迭代器" << endl;
//测试反向迭代器
set<int>::iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << endl;
--rit;
}
}
源码链接:https://gitee.com/zhang-hua1313/c-code/tree/master/review18