目录
前言
set 是 K模型的容器,map 是 KV模型的容器,但是它们的底层实现都是红黑树实现,即用红黑树可以封装出 set和 map,之前的篇章已经讲过红黑树了,这里就不解释了。
接下来对红黑树进行改造,用改造后的红黑树封装出 set 和 map
一、改造红黑树
使用的代码是之前篇章红黑树实现的代码,改造后红黑树的框架如下:
#pragma once
enum Colour
{
RED,
BLACK,
};
template<class T>
struct RBTreeNode
{
//构造函数
RBTreeNode(const T& data)
:_data(data)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_col(RED)
{}
//成员变量
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
};
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
typedef __RBTreeIterator<T, T&, T*> iterator;
//构造
__RBTreeIterator(Node* node)
:_node(node)
{}
// 普通迭代器的时候,他是拷贝构造
// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器
__RBTreeIterator(const iterator& s)
:_node(s._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)const
{
return _node != s._node;
}
bool operator==(const Self& s)const
{
return _node == s._node;
}
Self& operator++()
{
if (_node->_right)
{
Node* min = _node->_right;
while (min->_left)
{
min = min->_left;
}
_node = min;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node->_left)
{
Node* max = _node->_left;
while (max->_right)
{
max = max->_right;
}
_node = max;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
//成员变量
Node* _node;
};
template<class K, class T, class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __RBTreeIterator<T, T&, T*> iterator;
typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
iterator begin()
{
Node* left = _root;
while (left->_left)
{
left = left->_left;
}
return iterator(left);// 使用匿名对象构造返回
}
const_iterator begin()const
{
Node* left = _root;
while (left->_left)
{
left = left->_left;
}
return const_iterator(left);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator end()const
{
return const_iterator(nullptr);
}
//插入
pair<iterator, bool> Insert(const T& data)
{}
//中序遍历
void InOrder()
{
_InOrder(_root);
}
//检查红黑树特性
bool IsBalance()
{}
private:
//检查每条路径的黑色节点是否相等 && 是否出现连续红色节点
bool Check(Node* root, int blackNum, int ref)
{}
void _InOrder(Node* root)
{}
//左单旋
void RotateL(Node* parent)
{}
//右单旋
void RotateR(Node* parent)
{}
private:
Node* _root = nullptr;//缺省值
};
改造红黑树就是给红黑树增加迭代器相关的,这棵泛型的红黑树可以同时封装出set 和 map
1.1 红黑树迭代器相关
迭代器模板参数在 list 模拟实现已经有过解释,这里不解释了,模板参数 T 是红黑树存放数据的类型,下面解释
template<class T, class Ref, class Ptr>
上面迭代器类只需提供构造函数即可, 拷贝构造和赋值不需要自己实现,默认生成的即可,当前迭代器赋值给另一个迭代器是不需要深拷贝的,浅拷贝就可以,拷贝构造也是;析构释放空间的事情不归迭代器类管理,迭代器类只需构建迭代器对象即可
上面红黑树迭代器实现的接口有:
//构造
__RBTreeIterator(Node* node)
:_node(node)
{}
// 普通迭代器的时候,他是拷贝构造
// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器
__RBTreeIterator(const iterator& s)
:_node(s._node)
{}
//解引用,获取数据
Ref operator*(){}
//返回数据的地址
Ptr operator->(){}
//比较两个迭代器是否不相等
bool operator!=(const Self& s)const{}
//比较两个迭代器是否相等
bool operator==(const Self& s)const{}
//前置++
Self& operator++(){}
//前置--
Self& operator--(){}
说明一下,迭代器都是支持普通迭代器构造const迭代器
实现如下,这里它利用了构造函数和拷贝构造函数的特性,当传入的迭代器是普通迭代器时:参数类型与拷贝构造参数要求一致,它就是拷贝构造;当传入的的迭代器是 const迭代器,参数类型与拷贝构造函数不匹配,类型有差异,它就变成了构造函数
typedef __RBTreeIterator<T, T&, T*> iterator;
// 普通迭代器的时候,他是拷贝构造
// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器
__RBTreeIterator(const iterator& s)
:_node(s._node)
{}
前置++ 和前置-- 是红黑树迭代器实现的重点
前置++
一个结点的正向迭代器进行 ++ 操作后,应该根据红黑树中序遍历的序列找到当前结点的下一个结点
如下图,传入第一个节点是1,迭代器++ 下一个节点是6,再次++ 节点为8,迭代器要达到这种效果才可以满足需求
思路:
- 传入一个节点,如果节点右不为空就先往右边走,找到右边节点最小的节点,该节点就是 ++ 的下一个节点
- 传入一个节点,如果节点的右子树为空,去找祖先,迭代依次往上走,找到孩子是父亲左的那个祖先节点
代码如下:
Self& operator++()
{
if (_node->_right)//传入节点右不为空,找右边最小节点
{
Node* min = _node->_right;
while (min->_left)
{
min = min->_left;
}
_node = min;
}
else)//传入节点为空
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
前置--
一个结点的迭代器进行 -- 操作后,就是红黑树中序遍历找到的结点的前一个结点
如下图,比如传入第一个节点是27,迭代器-- 下一个节点是25,再次 -- 节点为22,迭代器要达到这种效果才可以满足需求
--的思路和 ++ 的思路类似,只不过是反过来找
思路:
- 传入一个节点,如果节点左不为空就先往左边走,找到左边节点最大的节点,该节点就是 -- 的下一个节点
- 传入一个节点,如果节点的左子树为空,去找祖先,迭代依次往上走,找到孩子是父亲右的那个祖先节点
代码如下:
Self& operator--()
{
if (_node->_left)//传入节点左不为空就先往左边走,
{
Node* max = _node->_left;
while (max->_right)
{
max = max->_right;
}
_node = max;
}
else//传入节点左为空
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
其他就不介绍了
1.2 红黑树接口相关
template<class K, class T, class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __RBTreeIterator<T, T&, T*> iterator;
typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
private:
Node* _root = nullptr;//缺省值
};
说明一下模板参数,为了实现泛型RBTree:
- 红黑树第一个参数是 K(Key),主要用于查找数据;
- 红黑树第二个模板参数 T ,可以识别是 map 还是 set,因为 set和 map传给 T的参数不一样,set 传的是 Key,map 传的是键值对 pair<K, V>;
- 红黑树第三个参数是 KeyOfT,这是一个仿函数,由于红黑树中 T 存的可能是K,也可能是pair<K, V>。那我们插入的时候该怎么比较结点的大小呢,对于set是没有问题的,直接可以用T比较,但是map就不行了,我们要取出pair<K, V>的 first来进行比较
二、set代码
#include "RBTree.h"
namespace fy
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
iterator begin()const
{
return _t.begin();
}
iterator end()const
{
return _t.end();
}
pair<iterator, bool> insert(const K& key)
{
pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
return pair<iterator, bool>(ret.first, ret.second);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
void TestSet()
{
int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
set<int> s;
for (auto& e : arr)
{
s.insert(e);
}
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
}
}
三、map代码
#include "RBTree.h"
namespace fy
{
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<const K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<const K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
const_iterator begin()const
{
return _t.begin();
}
const_iterator end()const
{
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 = insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
void TestMap()
{
int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
map<int, int> m;
for (auto& e : arr)
{
m.insert(make_pair(e, e));
}
map<int, int>::iterator it = m.begin();
while(it != m.end())
{
it->second++;
cout << it->first << "->" << it->second << endl;
++it;
}
map<string, int> countMap;
string str[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
for (auto& e : str)
{
countMap[e]++;
}
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
}
}
这篇文章写起来逻辑有点不通(难写)dog,主要是用来复习的
----------------我是分割线---------------
文章到这里就结束了,下一篇即将更新