提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!
提示:以下是本篇文章正文内容,下面案例可供参考
底层原理
set和map的底层使用红黑树封装的。
- set是key模型
- map是key value模型
set也是用key value红黑树模型来封装实现的
并不需要两颗红黑树,用同一个类模板的红黑树,传不同的模板参数来实现不同的红黑树模型。
为了让红黑树能够识别set和map,我们在红黑树的模板中增加一个模板参数T:
template<class K, class T>
class RBTree
因为T模板参数可能是键值K,也可能是由key 和 value 构成的键值对。
如果是set容器,那么它传入底层红黑树的模板参数就是Key和Key:
template<class K>
class set
{
private:
RBTree<K,K> _t;
};
如果是map容器,传入底层红黑树的模板参数就是Key和Key和value的键值对:
template<class K,class T>
class map
{
private:
RBTree<K, pair<const K,V>> _t;
};
通过上面,我们可以知道,对于set和map的区别:我们只要通过第二个模板参数就能进行区分,那是不是第一个模板参数就没有意义了呢?
- 对于insert(const Value&v)来说,需要放入存入的值,确实是这个样子的,插入的值是value,对于set就是key,对于map就是pair。
- 但是对于find(const Key&key)来说,查找的参数不是value,找的不是pair而是Key,对于map容器来说就不行了,对于set容器来说可以。
红黑树的节点
set容器:K和T都是键值Key;map容器:K是键值Key,T由Key和Value构成的键值对;
但是底层红黑树并不知道上层容器到底是map还是set,因此红黑树的结点当中直接存储T就行了,如果是set的时候,结点当中存储的是键值Key;如果是map的时候,结点当中存储的就是Key和Value构成的键值对,所以红黑树的结点定义如下,由T类型来决定红黑树存的是key还是pair:
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
T _data;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
仿函数
插入的时候data的大小如何去进行比较:对于set来说是key进行比较,对于map来说是pair来进行比较,但是红黑树模板并不知道传进来的是什么类型;对于按pair来进行比较,我们刚开始kv结构就直接用kv.first去进行比较的。
但是在库中:first进行比较或者first进行比较并且second进行比较,我们要的只是pair中的first来进行比较,库中的明显不符合我们的要求。
由于底层的红黑树不知道传的是map还是set容器,当需要进行两个结点键值的比较时,底层红黑树传入的仿函数来获取键值Key,进行两个结点键值的比较:这个时候我们就需要仿函数了,如果是set那就是用于返回T当中的键值Key,如果是map那就是用于返回pair的first:
需要类的对象像函数一样使用,就可以使用仿函数 ---> KeyOfT仿函数取出T对象中的key
我们有了仿函数时,查找函数就可以用仿函数来替换比较部分
KeyOfT kot;// 仿函数对象
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
// 对于set而言,就是key和key比较
// 对于map而言,就是pair和pair比较,但是我们要的是pair<K,V>类型创建的对象kv中的first进行比较
if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(iterator(cur), false);
}
}
迭代器
// 用一个类去封装节点的指针是因为节点的指针不符合我们的行为预期,所以我们封装了之后,
// 用重载运算符去控制节点的行为。
template<class T, class Ptr, class Ref>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T , Ptr, Ref> Self;
Node* _node;
RBTreeIterator(Node* node)
:_node(node)
{}
// *解引用操作
// 返回对应结点数据的引用:
Ref operator*()
{
return _node->_data;
}
// 给自定义类型的指针用的
Ptr operator->()
{
// 用来返回pair的指针
return &_node->_data;
}
Self& operator++()
{
if (_node->_right)
{
// 右子树的中序第一个(最左节点)
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else
{
// 右为空,找祖先里面孩子是父亲左的那个节点
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
// parent为空时,循环结束,走到根了
cur = parent;
parent = cur->_parent;
// cur == parent->left就停下来
}
// parent的左子树走完了,开始根和右子树
_node = parent;
}
return *this;
}
Self& operator--()
{
// 跟++的逻辑相反
return *this;
}
bool operator!=(const Self& s)
{
// 迭代器用节点的指针比较,指向节点的指针一样,就是指向的是同一个位置
return _node != s._node;
}
bool operator == (const Self & s)
{
return _node == s._node;
}
};
迭代器的++:
一个结点的正向迭代器进行++操作后,根据红黑树中序(左、根、右)找到当前结点的下一个结点,中序的第一个节点是最左,迭代器的++怎么去找:
- it指向节点,右不为空,下一个就是右子树的最左节点;
- it指向节点,右为空,意味着这个节点的子树中序访问完了,下一个节点找祖先里面孩子是父亲左的那个祖先。
begin(),end():
- begin():返回中序(左、根、右)第一个结点的正向迭代器,即最左节点,返回的是最左节点,直接找最左节点即可
- end():返回中序(左、根、右)最后一个结点下一个位置的正向迭代器,这里直接用空指针
// set->RBTree<K, K, SetKeyOfT>
// map->RBTree<K, pair<K, V>, MapKeyOfT>
// 第二个模板参数T决定红黑树中存放什么
// 需要类的对象像函数一样使用,就可以使用仿函数 ---> KeyOfT仿函数 取出T对象中的key
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;
const_iterator begin() const
{
Node* subLeft = _root;
while (subLeft && subLeft->_left)
{
subLeft = subLeft->_left;
}
return const_iterator(subLeft);
}
iterator begin()
{
Node* subLeft = _root;
while (subLeft && subLeft->_left)
{
subLeft = subLeft->_left;
}
return iterator(subLeft);
// 用subLeft节点指针调用RBTreeIterator<T>的构造函数(构造函数在上一个模板内)
}
iterator end()
{
return iterator(nullptr);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
Map代码
#pragma once
#include"RBTree.h"
namespace bit
{
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<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<K, V>& kv)
{
return _t.Insert(kv);
}
iterator find(const K& key)
{
return _t.Find(key);
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;// 中间还调用了operator->()函数返回pair类型的对象
}
private:
// map里面pair中的first不允许修改,second允许修改
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
void test_map1()
{
map<int, int> m;
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
m.insert(make_pair(e, e));
}
map<int, int>::iterator it = m.begin();
while (it != m.end())
{
// map是first不允许修改,second允许修改
//it->first += 100;
it->second += 100;
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
}
void test_map2()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "西瓜", "香蕉", "草莓" };
map<string, int> countMap;
for (auto& e : arr)
{
// 条件断点
/*if (e == "草莓")
{
int i = 0;
}*/
countMap[e]++;
}
// 范围for在底层转换成迭代器
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
}
}
Set代码
#pragma once
#include"RBTree.h"
namespace bit
{
template<class K>
class set
{
// 内部类
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, const K, SetKeyOfT>::iterator iterator;
// 在RBTree<K, const K, SetKeyOfT>类域中取iterator
// 加typename告诉编译器iterator是类型
typedef typename RBTree<K, const K, SetKeyOfT>::const_iterator const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
pair<iterator,bool> insert(const K& key)
{
return _t.Insert(key);
}
iterator find(const K& key)
{
return _t.Find(key);
}
private:
RBTree<K, const K, SetKeyOfT> _t;
};
void test_set1()
{
set<int> s;
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
s.insert(e);
}
set<int>::iterator it = s.begin();
while (it != s.end())
{
//if(*it % 2 == 0)
// *it += 100; // 会影响二叉树的规则
cout << *it << " ";
++it;
}
cout << endl;
}
}
红黑树代码
#pragma once
#include<vector>
enum Colour
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
T _data;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
// 用一个类去封装节点的指针是因为节点的指针不符合我们的行为预期,所以我们封装了之后,
// 用重载运算符去控制节点的行为。
template<class T, class Ptr, class Ref>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T , Ptr, Ref> Self;
Node* _node;
RBTreeIterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_data;
}
// 给自定义类型的指针用的
Ptr operator->()
{
// 用来返回pair的指针
return &_node->_data;
}
Self& operator++()
{
if (_node->_right)
{
// 右子树的中序第一个(最左节点)
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else
{
// 右为空,找祖先里面孩子是父亲左的那个节点
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
// parent为空时,循环结束,走到根了
cur = parent;
parent = cur->_parent;
// cur == parent->left就停下来
}
// parent的左子树走完了,开始根和右子树
_node = parent;
}
return *this;
}
Self& operator--()
{
// 跟++的逻辑相反
return *this;
}
bool operator!=(const Self& s)
{
// 迭代器用节点的指针比较,指向节点的指针一样,就是指向的是同一个位置
return _node != s._node;
}
bool operator == (const Self & s)
{
return _node == s._node;
}
};
// set->RBTree<K, K, SetKeyOfT>
// map->RBTree<K, pair<K, V>, MapKeyOfT>
// 第二个模板参数T决定红黑树中存放什么
// 需要类的对象像函数一样使用,就可以使用仿函数 ---> KeyOfT仿函数 取出T对象中的key
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;
const_iterator begin() const
{
Node* subLeft = _root;
while (subLeft && subLeft->_left)
{
subLeft = subLeft->_left;
}
return const_iterator(subLeft);
}
iterator begin()
{
Node* subLeft = _root;
while (subLeft && subLeft->_left)
{
subLeft = subLeft->_left;
}
return iterator(subLeft);
// 用subLeft节点指针调用RBTreeIterator<T>的构造函数(构造函数在上一个模板内)
}
iterator end()
{
return iterator(nullptr);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
// Find来使用第一个模板参数
iterator Find(const K& key)
{
KeyOfT kot;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else if (kot(cur->_data) > key)
{
cur = cur->_left;
}
else
{
return iterator(cur);// 调用iterator的构造函数
}
}
return end();
}
pair<iterator,bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root),true);
}
KeyOfT kot;// 仿函数对象
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
// 对于set而言,就是key和key比较
// 对于map而言,就是pair和pair比较,但是我们要的是pair<K,V>类型创建的对象kv中的first进行比较
if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(iterator(cur), false);
}
}
cur = new Node(data); // 红色的
Node* newnode = cur;// 插入新节点,提前保留一份,因为cur是红色,可能需要向上处理
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
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)
{
// g
// p u
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
// 情况一:叔叔存在且为红
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
// 情况二:叔叔不存在或者存在且为黑
// 旋转+变色
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode), true);
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* ppnode = parent->_parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_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;
subL->_right = parent;
Node* ppnode = parent->_parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subL;
}
else
{
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
}
private:
Node* _root = nullptr;
};
总结
好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。