目录
一、改造红黑树
map 和 set 的底层都是红黑树,虽然不是同一棵树,但都是由同一个模板实例化而成。因此我们希望改造红黑树,让他适配不同类型的数据。关联式容器中存储的是的键值对,k为key的类型,ValueType: 如果是map,则为pair; 如果是set,则为k
我们可以定义一个模板T,通过传模板参数来决定树里存的是什么数据类型。
【存在问题】
当我们插入或者查找一个节点时,红黑树里的比较逻辑都是按照pair里面的K值进行比较的,这里的红黑树存储的数据是T类型,当插入一个T类型的数据时,我们不知道这个数据是 K 类型还是pair<K,V>类型,这就无法进行比较
【解决方法】
我们可以利用一个仿函数,这个仿函数的功能是可以将数据里的K类型数据取出来。那么我们可以给红黑树增加一个模板参数,给仿函数用。一旦遇到要比较的逻辑时,我们就可以将数据里的K值取出来进行比较。
仿函数实现的原理:当T类型是K类型数据时,直接返回K值即可,当T类型是pair<K,V>数据时,返回里面的 first 数据即可(就是K值)。
二、红黑树的迭代器
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代 器,需要考虑红黑树的迭代器和链表的迭代器类似,都是自定义类型,因为原生指针不能满足需要,所以需要自定义。
1、operator++
中序遍历是左、根、右。
所以begin的位置肯定是在最左边,也就是最小值。
++指向下一个位置,也就是右子树的最左边,此时我们需要考虑右子树是否存在。
当右边有结点时,那么就找右边的最小值,也就是右边的最左结点。
当右边没有结点时,我们就要往上找(父亲结点&&左结点)的节点。因为当右边没有结点时,说明右边已经访问完了,那么往上找凡是父亲结点是祖先右边时就直接跳过,当父亲结点是祖先左边时,那么这个祖先结点就是下一次要访问的结点。
2、operator--
跟++倒着来就可以了。遍历顺序是右、根、左。
--就是到上一个比它小的位置。因此我们要讨论的是当前结点的左边是否有结点。
当左边有结点时,我们就找左边的最大值,也就是左边的最右结点。
当左边没有结点时,我们就找父亲结点为右结点的那个祖先。
3、map-operator [ ]
map的[ ]运算符重载,底层实现本质是调用了insert函数。然后通过insert函数返回的pair<iterator,bool>类型数据来找到Value值。所以在实现[ ]运算符重载时,我们需要对红黑树里的insert进行改造,因为原来的insert的返回值是布尔值,我们需要pair类型返回值。
红黑树的insert改造后,那么set和map里的insert都需要修改,因为底层用的就是红黑树的insert。
4、const 迭代器
与链表的const迭代器实现原理一样,我们通过三个模板参数(template <class T,class Ref,class Ptr>)来控制函数的返回值,从而控制返回的是普通类型的迭代器还是const类型的迭代器。【链表迭代器】
Ref控制解引用函数的返回值,当Ref为T&时,返回的就是普通迭代器,当Ref为const T&时,返回的就是const迭代器。
Ptr控制的->重载函数的返回值,当Ptr为T时,返回的就是普通迭代器,当Ptr为const T时,返回的就是const迭代器。
5、迭代器的begin()和end()
通过传不同的模板参数,来是实例化出我们想要的迭代器:普通迭代器和const迭代器。
红黑树的迭代器begin()是要求在最小结点那个位置的迭代器,即最左结点,找到最左结点后,我们就用该结点的指针构造迭代器即可。
红黑树的迭代器end()可以看作是nullptr的位置,因为end()是最后一个节点的下一个位置,当找不到父亲时,就说明到尽头了。
const迭代器的begin()和end()与普通迭代器的begin()和end()是一样的,就是构造时用const迭代器构造,和返回const迭代器
三、封装 map 和 set
1、提取 k 的值
set的底层结构就是红黑树,因此在set中直接封装一棵红黑树,储存的是pair<k,k>
map的底层结构就是红黑树,因此在map中直接封装一棵红黑树,储存的是pair<k,v>
2、map 和set 的迭代器
在重命名红黑树里的迭代器时,需要在类名前面加上 typename,因为这时类模板还没有实例化出对象出来,就算实例化了,也有部分类型没有实例,因为编译器也不知道这个是内置类型还是静态变量,typename 是告诉编译器这个是类型,这个类型在类模板里定义,等类模板实例化后再找。
定义好普通迭代和const迭代器后,就可以实现begin()和end()了。
四、key值不能修改的问题
1、set
【存在问题】
迭代器的解引用是可以修改的,一旦修改可能不是二叉树:
set 的 key 值、使用迭代器修改:
为了保证二叉树的结构,我们希望:set里的key值不能被修改。map里的key值不能被修改,但value值可以被修改。
【解决方法】
set 里存储的值就只有Key值,索性我们直接让这个存储的数据无法被修改,只能访问读取,无法修改。所以我们让普通迭代器变成const迭代器即可。所以在set里,我们将普通迭代器和const迭代器都设为const迭代器。
但此时会遇到一个问题,begin() 和 end() 的返回类型是const 迭代器,但是_t.begin()是普通迭代器,这样就会类型不匹配。因此我们需要定义一个构造函数将普通迭代器和const迭代器相转换。
2、map
map的解决原理
在存储的时候就让K值无法修改。
因为我们知道map里存储的数据是pair<K,V>类型,我们不能想set那个让普通迭代器变成const迭代器,因为map要求Value的值还是可以修改的,所以不让pair<K,V>类型无法修改,而是单纯的让里面的K值无法修改,也就是在里面用const修饰K,那么这样K值就不能被修改,V值可以被修改。
pair是可以修改的,但是里面的K是无法被修改的!
五、完整代码
RBTree.h
#pragma once
enum Colour
{
RED,
BLACK,
};
template<class T>//定义一个模板参数 T
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
//用T来取代关联时容器 pair<K, V>
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>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator<T, T&, T*> itertaor; 拷贝构造
// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
// 支持普通迭代器构造const迭代器的构造函数
__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
:_node(it._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++()
{
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else
{
// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node->_left)
{
// 1、左不为空,找左子树最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else
{
// 2、左为空,孩子是父亲的右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
// 仿函数
template<class K, class T, class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
public:
typedef __RBTreeIterator<T, T&, T*> itertaor;
typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
itertaor begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
//返回最小节点
return itertaor(cur);
}
itertaor end()
{
return itertaor(nullptr);
}
const_itertaor begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return const_itertaor(cur);
}
const_itertaor end() const
{
return const_itertaor(nullptr);
}
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else if (kot(cur->_data) > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
pair<itertaor, bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
//插入成功则返回的是新结点指针构造的迭代器封装的pair类型,bool值为true
return make_pair(itertaor(_root), true);
}
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
//插入失败,返回的是已经存在结点的迭代器构造的pair类型,bool值是false
return make_pair(itertaor(cur), false);
}
}
cur = new Node(data);
Node* newnode = cur;
if (kot(parent->_data) > kot(data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// p u
// c
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
else // (grandfather->_right == parent)
{
// g
// u p
// c
Node* uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(itertaor(newnode), true);;
}
bool IsBalance()
{
if (_root && _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);
}
int Height()
{
return _Height(_root);
}
private:
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (benchmark != blackNum)
{
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
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 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;
}
}
private:
Node* _root = nullptr;
};
Map.h
#define _CRT_SECURE_NO_WARNINGS
#include"RBTree.h"
namespace zhou {
template<class K, class V>
class map
{
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::itertaor iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
//已经存在就就不插入,不存在就插入,默认给V的缺省参数
//如果key在树里已经存在那么就返回已经存在结点的迭代器
//如果key不在树里,那么就将该结点插入进去,返回该结点的迭代器
return ret.first->second;
//first先访问里面的迭代器,迭代器的second才是Value值。
}
pair<iterator, bool> insert(const pair<const K, V>& kv)
{
return _t.Insert(kv);
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
};
}
Set.h
#define _CRT_SECURE_NO_WARNINGS
#include"RBTree.h"
namespace zhou
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::const_itertaor iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_itertaor const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
pair<iterator, bool> insert(const K& key)
{
//这里insert返回的是树里的普通迭代器_treeIterator<T,T&,T*> iterator
return _t.Insert(key);
//用普通迭代器构造const迭代器
//所以我们需要写一个迭代器的构造函数,可以用迭代器构造迭代器
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
void test_set1()
{
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
set<int> s;
for (auto e : a)
{
s.insert(e);
}
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
//*it = 1;
++it;
}
cout << endl;
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
}