第19讲:封装红黑树实现 mymap 和 myset

目录

  • [1. 源码及框架分析](#1. 源码及框架分析)
  • [2. 模拟实现 map 和 set](#2. 模拟实现 map 和 set)
  • [2.1 实现出复用红黑树的框架,并支持 insert](#2.1 实现出复用红黑树的框架,并支持 insert)
  • [2.2 支持 iterator 的实现](#2.2 支持 iterator 的实现)
  • [2.3 map 支持 []](#2.3 map 支持 [])
  • [2.4 bit::map 和 bit::set 代码实现](#2.4 bit::map 和 bit::set 代码实现)

1. 源码及框架分析

SGI-STL30 版本源代码中,map 和 set 的源代码位于 map/set/stl_map.h、stl_set.h、stl_tree.h 等几个头文件中。

map 和 set 的实现结构框架核心部分截取如下:

cpp 复制代码
// set
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>

// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>

// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
// typedefs:
typedef Key key_type;
typedef Key value_type;
private:
typedef rb_tree<key_type, value_type,
identity<value_type>, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing set
};

// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
// typedefs:
typedef Key key_type;
typedef T mapped_type;
typedef pair<const Key, T> value_type;
private:
typedef rb_tree<key_type, value_type,
select1st<value_type>, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing map
};

// stl_tree.h
struct __rb_tree_node_base
{
typedef __rb_tree_color_type color_type;
typedef __rb_tree_node_base* base_ptr;

color_type color;
base_ptr parent;
base_ptr left;
base_ptr right;
};

// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc = alloc>
class rb_tree {
protected:
typedef void* void_pointer;
typedef __rb_tree_node_base* base_ptr;
typedef __rb_tree_node<Value> rb_tree_node;
typedef rb_tree_node* link_type;
typedef Key key_type;
typedef Value value_type;
public:
// insert用的是第二个模板参数左形参
pair<iterator,bool> insert_unique(const value_type& x);

// erase和find用第一个模板参数做形参
size_type erase(const key_type& x);
iterator find(const key_type& x);
protected:
size_type node_count; // keeps track of size of tree
link_type header;
};

template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field;
};
  • 通过下图对框架的分析,我们可以看到源码中 rb_tree 使用了一个巧妙的泛型思想实现。rb_tree 是实现 key 的搜索场景,还是 key/value 的搜索场景,并不是直接写死的,而是由第二个模板参数 Value 决定 _rb_tree_node 中存储的数据类型。
  • set 实例化 rb_tree 时,第二个模板参数给的是 key;map 实例化 rb_tree 时,第二个模板参数给的是 pair<const key, T>。这样一棵红黑树既可以实现 key 搜索场景的 set,也可以实现 key/value 搜索场景的 map。
  • 要注意,源码里面模板参数是用 T 代表 value,而内部写的 value_type 不是我们日常 key/value 场景中所说的 value;源码中的 value_type 反而是红黑树结点中存储的真实数据类型。
  • rb_tree 的第二个模板参数 Value 已经控制了红黑树结点中存储的数据类型,为什么还要传第一个模板参数 Key 呢?尤其是 set,两个模板参数是一样的,这是很多同学学习时的一个疑问。要注意,对于 map 和 set,find/erase 时的函数参数都是 Key,所以第一个模板参数是传给 find/erase 等函数作为形参的类型。对于 set 而言两个参数是一样的,但是对于 map 而言就完全不一样了:map 插入的是 pair 对象,但是 find 和 erase 的是 Key 对象。
  • 吐槽一下,这里源码命名风格比较乱:set 模板参数用的是 Key,map 用的是 Key 和 T,而 rb_tree 用的又是 Key 和 Value。可见大佬有时写代码也不规范,乱弹琴。

2. 模拟实现 map 和 set

2.1 实现出复用红黑树的框架,并支持 insert

  • 参考源码框架,map 和 set 复用之前实现的红黑树。
  • 这里相比源码调整一下,key 参数使用 K,value 参数使用 V,红黑树中的数据类型使用 T。
  • 因为 RBTree 实现了泛型,不知道 T 参数是 K 还是 pair<K, V>,所以 insert 内部进行插入逻辑比较时就没办法直接比较。因为 pair 默认支持 key 和 value 一起参与比较,而我们需要的是任何时候只比较 key,所以在 map 和 set 层分别实现一个 MapKeyOfT 和 SetKeyOfT 仿函数,传给 RBTree 的 KeyOfT。然后 RBTree 中通过 KeyOfT 仿函数取出 T 类型对象中的 key,再进行比较,具体细节参考下面的代码实现。
cpp 复制代码
// 源码中pair支持的<重载实现
template <class T1, class T2>
bool operator< (const pair<T1,T2>& lhs, const pair<T1,T2>& rhs)
{ return lhs.first<rhs.first || (!(rhs.first<lhs.first) && lhs.second<rhs.second); }

// Mymap.h
namespace bit
{
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:
bool insert(const pair<K, V>& kv)
{
return _t.Insert(kv);
}
private:
RBTree<K, pair<K, V>, MapKeyOfT> _t;
};
}

// Myset.h
namespace bit
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
bool insert(const K& key)
{
return _t.Insert(key);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}

// RBTree.h
enum Colour
{
RED,
BLACK
};

template<class T>
struct RBTreeNode
{
T _data;

RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;

RBTreeNode(const T& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};

// 实现步骤:
// 1、实现红黑树
// 2、封装map和set框架,解决KeyOfT
// 3、iterator
// 4、const_iterator
// 5、key不支持修改的问题
// 6、operator[]
template<class K, class T, class KeyOfT>
class RBTree
{
private:
typedef RBTreeNode<T> Node;
Node* _root = nullptr;

public:
bool Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return 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
{
return false;
}
}

cur = new Node(data);
Node* newnode = cur;

// 新增结点。颜色给红色
cur->_col = RED;
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;

//...

return true;
}
}

2.2 支持 iterator 的实现

iterator 核心源代码
cpp 复制代码
struct __rb_tree_base_iterator
{
typedef __rb_tree_node_base::base_ptr base_ptr;
base_ptr node;

void increment()
{
if (node->right != 0) {
node = node->right;
while (node->left != 0)
node = node->left;
}
else {
base_ptr y = node->parent;
while (node == y->right) {
node = y;
y = y->parent;
}
if (node->right != y)
node = y;
}
}

void decrement()
{
if (node->color == __rb_tree_red &&
node->parent->parent == node)
node = node->right;
else if (node->left != 0) {
base_ptr y = node->left;
while (y->right != 0)
y = y->right;
node = y;
}
else {
base_ptr y = node->parent;
while (node == y->left) {
node = y;
y = y->parent;
}
node = y;
}
}
};

template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{
typedef Value value_type;
typedef Ref reference;
typedef Ptr pointer;
typedef __rb_tree_iterator<Value, Value&, Value*>  iterator;
__rb_tree_iterator() {}
__rb_tree_iterator(link_type x) { node = x; }
__rb_tree_iterator(const iterator& it) { node = it.node; }

reference operator*() const { return link_type(node)->value_field; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

self& operator++() { increment(); return *this; }
self& operator--() { decrement(); return *this; }

inline bool operator==(const __rb_tree_base_iterator& x,
const __rb_tree_base_iterator& y) {
return x.node == y.node;
}

inline bool operator!=(const __rb_tree_base_iterator& x,
const __rb_tree_base_iterator& y) {
return x.node != y.node;
iterator 实现思路分析
  • iterator 实现的大框架跟 list 的 iterator 思路是一致的,用一个类型封装结点的指针,再通过重载运算符实现迭代器像指针一样访问的行为。
  • 这里的难点是 operator++ 和 operator-- 的实现。前面分析过,map 和 set 的迭代器走的是中序遍历:左子树 -> 根结点 -> 右子树。那么 begin() 会返回中序第一个结点的 iterator,也就是 10 所在结点的迭代器。
  • 迭代器 ++ 的核心逻辑就是不看全局,只看局部,只考虑当前中序局部要访问的下一个结点。
  • 迭代器 ++ 时,如果 it 指向的结点的右子树不为空,代表当前结点已经访问完了,要访问的下一个结点是右子树的中序第一个。一棵树中序第一个是最左结点,所以直接找右子树的最左结点即可。
  • 迭代器 ++ 时,如果 it 指向的结点的右子树为空,代表当前结点已经访问完了,并且当前结点所在的子树也访问完了。要访问的下一个结点在当前结点的祖先里面,所以要沿着当前结点到根的祖先路径向上找。
  • 如果当前结点是父亲的左,根据中序左子树 -> 根结点 -> 右子树,那么下一个访问的结点就是当前结点的父亲。例如:it 指向 25,25 右为空,25 是 30 的左,所以下一个访问的结点就是 30。
  • 如果当前结点是父亲的右,根据中序左子树 -> 根结点 -> 右子树,当前结点所在的子树访问完了,当前结点所在父亲的子树也访问完了,那么下一个访问的结点需要继续往根的祖先中去找,直到找到孩子是父亲左的那个祖先,它就是中序要访问的下一个结点。例如:it 指向 15,15 右为空,15 是 10 的右,15 所在子树访问完了,10 所在子树也访问完了,继续往上找,10 是 18 的左,那么下一个访问的结点就是 18。
  • end() 如何表示呢?如下图:当 it 指向 50 时,++it 时,50 是 40 的右,40 是 30 的右,30 是 18 的右,18 到根没有父亲,没有找到孩子是父亲左的那个祖先,这时父亲为空了,那我们就把 it 中的结点指针置为 nullptr,用 nullptr 充当 end。需要注意的是,STL 源码中红黑树增加了一个哨兵位头结点作为 end(),这个哨兵位头结点和根互为父亲,左指向最左结点,右指向最右结点。相比我们用 nullptr 作为 end(),差别不大,它能实现的,我们也能实现。只是 --end() 判断到结点为空时,需要特殊处理一下,让迭代器结点指向最右结点,具体参考迭代器 -- 的实现。
  • 迭代器 -- 的实现跟 ++ 的思路完全类似,逻辑正好反过来即可,因为它访问顺序是右子树 -> 根结点 -> 左子树,具体参考下面的代码实现。
  • set 的 iterator 也不支持修改,我们把 set 的第二个模板参数改成 const K 即可:RBTree<K, const K, SetKeyOfT> _t;
  • map 的 iterator 不支持修改 key 但是可以修改 value,我们把 map 的第二个模板参数 pair 的第一个参数改成 const K 即可:RBTree<K, pair<const K, V>, MapKeyOfT> _t;
  • 支持完整的迭代器还有很多细节需要修改,具体参考下面的代码。

2.3 map 支持 []

  • map 要支持 [],主要需要修改 insert 返回值支持,修改 RBTree 中的 insert 返回值为:

    pair<Iterator, bool> Insert(const T& data)

  • 有了 insert 支持,[] 实现就很简单了,具体参考下面代码实现。

2.4 bit::map 和 bit::set 代码实现

cpp 复制代码
// Myset.h
#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;
typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator 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 K& key)
{
return _t.Insert(key);
}

iterator find(const K& key)
{
return _t.Find(key);
}

private:
RBTree<K, const K, SetKeyOfT> _t;
};

void Print(const set<int>& s)
{
set<int>::const_iterator it = s.end();
while (it != s.begin())
{
--it;
// 不支持修改
//*it += 2;

cout << *it << " ";
}
cout << endl;
}

void test_set()
{
set<int> s;
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
s.insert(e);
}

for (auto e : s)
{
cout << e << " ";
}
cout << endl;

Print(s);
}
}

// Mymap.h
#include"RBTree.h"
namespace bit
{
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>::Iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator 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;
}

private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

void test_map()
{
map<string, string> dict;
dict.insert({ "sort", "排序" });
dict.insert({ "left", "左边" });
dict.insert({ "right", "右边" });

dict["left"] = "左边,剩余";
dict["insert"] = "插入";
dict["string"];

map<string, string>::iterator it = dict.begin();
while (it != dict.end())
{
// 不能修改first,可以修改second
//it->first += 'x';
it->second += 'x';

cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
}
}

// RBtree.h
enum Colour
{
RED,
BLACK
};

template<class T>
struct RBTreeNode
{
T _data;

RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;

RBTreeNode(const T& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, Ref, Ptr> Self;

Node* _node;
Node* _root;

RBTreeIterator(Node* node, Node* root)
:_node(node)
,_root(root)
{}

Self& operator++()
{
if (_node->_right)
{
// 右不为空,右子树最左结点就是中序第一个
Node* leftMost = _node->_right;
while (leftMost->_left)
{
leftMost = leftMost->_left;
}

_node = leftMost;
}
else
{
// 孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}

_node = parent;
}

return *this;
}

Self& operator--()
{
if (_node == nullptr) // end()
{
// --end(),特殊处理,走到中序最后一个结点,整棵树的最右结点
Node* rightMost = _root;
while (rightMost && rightMost->_right)
{
rightMost = rightMost->_right;
}

_node = rightMost;
}
else if (_node->_left)
{
// 左子树不为空,中序左子树最后一个
Node* rightMost = _node->_left;
while (rightMost->_right)
{
rightMost = rightMost->_right;
}

_node = rightMost;
}
else
{
// 孩子是父亲右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}

_node = parent;

}

return *this;
}

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;
}
};

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*> ConstIterator;

Iterator Begin()
{
Node* leftMost = _root;
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}

return Iterator(leftMost, _root);
}

Iterator End()
{
return Iterator(nullptr, _root);
}

ConstIterator Begin() const
{
Node* leftMost = _root;
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}

return ConstIterator(leftMost, _root);
}

ConstIterator End() const
{
return ConstIterator(nullptr, _root);
}

RBTree() = default;

~RBTree()
{
Destroy(_root);
_root = nullptr;
}

pair<Iterator, bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(Iterator(_root, _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
{
return make_pair(Iterator(cur, _root), false);
}
}

cur = new Node(data);
Node* newnode = cur;

// 新增结点,颜色给红色
cur->_col = RED;
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;
//   g
//  p  u
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
// u存在且为红  -》变色再继续往上处理
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;

cur = grandfather;
parent = cur->_parent;
}
else
{
// u存在且为黑或不存在    -》旋转+变色
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
{
//   g
// u   p
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, _root), true);
}

Iterator Find(const K& key)
{
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, _root);
}
}

return End();
}

private:
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;

parent->_right = subRL;
if (subRL)
subRL->_parent = parent;

Node* parentParent = parent->_parent;

subR->_left = parent;
parent->_parent = subR;

if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}

subR->_parent = parentParent;
}
}

void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;

parent->_left = subLR;
if (subLR)
subLR->_parent = parent;

Node* parentParent = parent->_parent;

subL->_right = parent;
parent->_parent = subL;

if (parentParent == nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}

subL->_parent = parentParent;
}

}

void Destroy(Node* root)
{
if (root == nullptr)
return;

Destroy(root->_left);
Destroy(root->_right);
delete root;
}

private:
Node* _root = nullptr;
};
相关推荐
孙鹏宇.1 小时前
C++类型转换
c++
超级架构师2 小时前
让 COBOL 长出 AI 大脑:原生 Agent 核心框架 praxo-core 深度剖析
c语言·开发语言·c++·人工智能·python·架构·ai编程
·present·2 小时前
CSAPP(lecture 10)
c语言·c++
Non-existent9872 小时前
在 WPS/Excel 里做地理数据处理:功能市场 + 奥维 / Google Earth / QGIS 三种跳转实战
开发语言·javascript·ecmascript
YLCHUP2 小时前
【模拟&STL】题解:AT_abc203_e [ABC203E] White Pawn_基础算法_数据结构_STL模板库_AtcoderABC_C++算法竞赛
数据结构·c++·算法·leetcode·动态规划·哈希算法·近邻算法
xiaolang_8616_wjl2 小时前
c++游戏_宝可梦大世界(源代码)
开发语言·c++·游戏
Leinwin2 小时前
Copilot Studio 实战:多模型选择、GitHub Copilot harness 计费与 Copilot Credits 额度管理
开发语言·c#
余额瞒着我当琳2 小时前
C++异常,异常的概念,异常抛出与捕获,RAII 和智能指针的设计思路,C++标准库一些指针的使用
java·开发语言·c++·算法