
首先我们可以复用上一节写出的红黑树代码,在原有基础上略作修改即可,这里我们只对map和set的迭代器功能实现做讲解,并不是完全实现,目的是为了深化学习map和set的底层原理
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
};
这段代码是为了同时实现 std::map 和 std::set 的红黑树结构而定义的迭代器。在STL中, std::map 和 std::set 都使用红黑树作为底层数据结构,但它们之间有一些差异,主要是在元素类型和键-值对之间。
这个迭代器定义的目的是为了在不重复实现两种不同容器的迭代器的情况下,实现红黑树的通用迭代器,以便它可以同时用于 std::map 和 std::set。让我解释一下为什么需要这样的定义:
T,无论是 std::map 的键值对还是 std::set 的元素。这种通用性是非常有价值的,因为它允许你在不同的上下文中使用相同的迭代器。std::map 的迭代器一样使用 std::set 的迭代器,或者反之。这使得STL更容易理解和使用。因此,这个迭代器定义的目的是为了实现通用性、代码重用和一致性,以提供更好的STL体验。在STL中,通常会看到这种类型的通用迭代器,以简化不同容器的实现。
__RBTreeIterator(Node* node)
:_node(node)
{}
这个构造函数是 __RBTreeIterator 迭代器的构造函数,它接受一个指向红黑树节点的指针作为参数,并将这个指针存储在 _node 成员变量中。
构造函数的主要目的是初始化迭代器的状态,使其指向红黑树中的特定节点,从而使迭代器能够正确地遍历红黑树中的元素。在迭代器创建时,你可以传入一个节点指针,它将成为迭代器的起始位置。
这种构造函数的设计允许你在创建迭代器时指定起始位置,以便从红黑树中的特定节点开始遍历。这在实现红黑树的迭代器时是常见的设计,因为你可能希望从根节点、某个特定节点或其他位置开始遍历树。
Ref operator*()
{
return _node->_data;
}
这个 operator* 函数是迭代器的解引用操作符重载函数。它的目的是返回当前迭代器指向的元素的引用,允许你通过迭代器来访问元素的值。
在这个具体的实现中,_node->_data 是红黑树节点中存储的元素值,它的类型是 T。operator* 函数返回的是这个元素值的引用,所以你可以通过迭代器来读取或修改当前节点的值。
通常情况下,解引用操作符重载允许你以类似于指针的方式来访问迭代器指向的对象,这使得使用迭代器遍历容器时,你可以像访问容器元素一样来访问数据,增强了代码的可读性和易用性。在STL和C++中,迭代器的行为通常模仿指针的行为,因此你可以使用类似于指针的语法来操作元素。
Ptr operator->()
{
return &_node->_data;
}
这个 operator-> 函数是迭代器的箭头成员访问操作符重载函数。它的目的是返回一个指向当前迭代器指向的元素的指针,以便你可以通过迭代器来访问元素的成员。
在这个具体的实现中,_node->_data 是红黑树节点中存储的元素值,它的类型是 T。operator-> 函数返回的是一个指向这个元素值的指针,所以你可以使用箭头操作符来访问元素的成员变量或调用其成员函数。
这个操作符的重载允许你以一种更直接的方式来访问迭代器指向的元素的成员。这在需要访问元素的内部数据或调用其方法时非常有用。
例如,如果你的元素是一个自定义类的对象,你可以使用 -> 操作符来访问该对象的成员函数,就像你正在操作该对象一样。这提供了一种更自然、更便捷的访问元素的方式。
bool operator!=(const Self& s) const
{
return _node != s._node;
}
这个 operator!= 函数是不等于运算符的重载函数。它的目的是比较当前迭代器和另一个迭代器 s 是否不相等。
在这个具体的实现中,它比较了 _node 成员变量,如果两个迭代器的 _node 不相等,就返回 true,表示它们不相等;否则,返回 false,表示它们相等。
这个操作符的重载使得你可以使用不等于运算符 != 来比较两个迭代器,以确定它们是否指向不同的位置。这对于循环遍历容器中的元素或判断迭代器是否达到容器末尾非常有用。通常情况下,你可以使用这个操作符来控制循环的终止条件。
bool operator==(const Self& s) const
{
return _node == s._node;
}
这个 operator== 函数是等于运算符的重载函数。它的目的是比较当前迭代器和另一个迭代器 s 是否相等。
在这个具体的实现中,它比较了 _node 成员变量,如果两个迭代器的 _node 相等,就返回 true,表示它们相等;否则,返回 false,表示它们不相等。
这个操作符的重载使得你可以使用等于运算符 == 来比较两个迭代器,以确定它们是否指向相同的位置。这对于检查迭代器是否相等以及判断是否达到容器末尾非常有用。通常情况下,你可以使用这个操作符来执行迭代器的相等性检查。
Self& operator++()
{
if (_node->_right)
{
// 下一个就是右子树的最左节点
Node* left = _node->_right;
while (left->_left)
{
left = left->_left;
}
_node = left;
}
else
{
// 找祖先里面孩子不是祖先的右的那个
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
这个 operator++ 函数是前置自增运算符的重载函数,用于使迭代器指向下一个元素。在红黑树的迭代器中,它实现了迭代器的前进操作。
这个函数的工作方式如下:
这个函数的目的是使迭代器指向下一个元素,以实现对红黑树的顺序遍历。在STL中,前置自增操作符通常用于移动迭代器到容器中的下一个元素。
Self& operator--()
{
if (_node->_left)
{
// 下一个是左子树的最右节点
Node* right = _node->_left;
while (right->_right)
{
right = right->_right;
}
_node = right;
}
else
{
// 孩子不是父亲的左的那个祖先
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
这个 operator-- 函数是前置自减运算符的重载函数,用于使迭代器指向前一个元素。在红黑树的迭代器中,它实现了迭代器的后退操作。
这个函数的工作方式与 operator++ 函数相似,但是方向相反:
这个函数的目的是使迭代器指向前一个元素,以实现对红黑树的逆序遍历。在STL中,前置自减操作符通常用于向前移动迭代器到容器中的前一个元素。
template<class K, class T, class KeyOfT>
struct RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __RBTreeIterator<T, T&, T*> iterator;
private:
Node* _root = nullptr;
};
这里就是在红黑树结构体中将模板迭代器重命名为iterator
iterator begin()
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return iterator(left);
}
iterator end()
{
return iterator(nullptr);
}
这两个函数是用于创建红黑树迭代器的成员函数,通常用于在你的红黑树容器类中实现迭代器的 begin() 和 end() 函数。
begin() 函数用于返回指向红黑树中最小元素的迭代器。它通过从根节点开始,沿着左子树的路径一直向下移动,直到找到最左边的叶子节点,这个叶子节点就是最小的元素。然后它创建一个迭代器对象,将这个最小元素的节点指针传递给迭代器的构造函数,最后返回这个迭代器。end() 函数用于返回表示红黑树结束位置的迭代器。通常情况下,结束位置是一个空指针(nullptr)。这个函数直接创建一个迭代器对象,将空指针传递给迭代器的构造函数,然后返回这个迭代器。这两个函数的实现使得你可以使用标准的迭代器语法来遍历红黑树中的元素。例如,你可以使用 begin() 获取指向第一个元素的迭代器,然后使用 end() 获取表示结束位置的迭代器,然后在循环中递增迭代器以遍历整个红黑树。这与STL容器的迭代器用法非常相似,使得在处理红黑树时更加方便和统一。
pair<iterator, bool> Insert(const T& data)
{
KeyOfT kot;
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root), true);
}
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), 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* grandfater = parent->_parent;
assert(grandfater);
assert(grandfater->_col == BLACK);
// 关键看叔叔
if (parent == grandfater->_left)
{
Node* uncle = grandfater->_right;
// 情况一 : uncle存在且为红,变色+继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
// 继续往上处理
cur = grandfater;
parent = cur->_parent;
}// 情况二+三:uncle不存在 + 存在且为黑
else
{
// 情况二:右单旋+变色
if (cur == parent->_left)
{
RotateR(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
// 情况三:左右单旋+变色
// g
// p u
// c
RotateL(parent);
RotateR(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
else // (parent == grandfater->_right)
{
Node* uncle = grandfater->_left;
// 情况一
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
// 继续往上处理
cur = grandfater;
parent = cur->_parent;
}
else
{
// 情况二:左单旋+变色
if (cur == parent->_right)
{
RotateL(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
// 情况三:右左单旋+变色
RotateR(parent);
RotateL(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode), true);
}
修改后它的返回值是一个 pair,其中包含一个迭代器和一个布尔值,用于表示插入是否成功。
函数的主要步骤:
_root,将其染成黑色(根节点必须是黑色),然后返回一个包含指向新节点的迭代器和 true 的 pair,表示插入成功。KeyOfT 对象 kot 来比较当前节点和要插入的数据,以确定应该将新节点插入到左子树还是右子树,或者是否已经存在相同的数据。如果找到相同的数据,函数会返回一个包含指向相同数据的迭代器和 false 的 pair,表示插入失败。cur,将其染成红色,然后将其插入到适当的位置,这与标准的二叉搜索树插入操作相似。_root 的颜色强制设置为黑色,以确保根节点始终为黑色。然后返回一个包含指向新插入节点的迭代器和 true 的 pair,表示插入成功。这个函数的实现遵循红黑树的插入规则和修正策略,以保持红黑树的平衡和性质。它返回一个迭代器,可以用于访问新插入的元素,以及一个布尔值,指示插入是否成功。
#pragma once
#include
#include
#include
using namespace std;
enum Colour
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
Colour _col;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
{}
};
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)
{}
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* left = _node->_right;
while (left->_left)
{
left = left->_left;
}
_node = left;
}
else
{
// 找祖先里面孩子不是祖先的右的那个
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node->_left)
{
// 下一个是左子树的最右节点
Node* right = _node->_left;
while (right->_right)
{
right = right->_right;
}
_node = right;
}
else
{
// 孩子不是父亲的左的那个祖先
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
template<class K, class T, class KeyOfT>
struct RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __RBTreeIterator<T, T&, T*> iterator;
iterator begin()
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return iterator(left);
}
iterator end()
{
return iterator(nullptr);
}
pair<iterator, bool> Insert(const T& data)
{
KeyOfT kot;
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root), true);
}
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), 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* grandfater = parent->_parent;
assert(grandfater);
assert(grandfater->_col == BLACK);
// 关键看叔叔
if (parent == grandfater->_left)
{
Node* uncle = grandfater->_right;
// 情况一 : uncle存在且为红,变色+继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
// 继续往上处理
cur = grandfater;
parent = cur->_parent;
}// 情况二+三:uncle不存在 + 存在且为黑
else
{
// 情况二:右单旋+变色
if (cur == parent->_left)
{
RotateR(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
// 情况三:左右单旋+变色
RotateL(parent);
RotateR(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
else // (parent == grandfater->_right)
{
Node* uncle = grandfater->_left;
// 情况一
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
// 继续往上处理
cur = grandfater;
parent = cur->_parent;
}
else
{
// 情况二:左单旋+变色
if (cur == parent->_right)
{
RotateL(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
// 情况三:右左单旋+变色
RotateR(parent);
RotateL(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode), true);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool IsBalance()
{
if (_root == nullptr)
{
return true;
}
if (_root->_col == RED)
{
cout << "根节点不是黑色" << endl;
return false;
}
// 黑色节点数量基准值
int benchmark = 0;
return PrevCheck(_root, 0, benchmark);
}
private:
bool PrevCheck(Node* root, int blackNum, int& benchmark)
{
if (root == nullptr)
{
if (benchmark == 0)
{
benchmark = blackNum;
return true;
}
if (blackNum != benchmark)
{
cout << "某条黑色节点的数量不相等" << endl;
return false;
}
else
{
return true;
}
}
if (root->_col == BLACK)
{
++blackNum;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "存在连续的红色节点" << endl;
return false;
}
return PrevCheck(root->_left, blackNum, benchmark)
&& PrevCheck(root->_right, blackNum, benchmark);
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
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 (_root == parent)
{
_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;
}
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
}
private:
Node* _root = nullptr;
};
#pragma once
#include "RBTree.hpp"
namespace yulao
{
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<K, V>, MapKeyOfT>::iterator iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
pair<iterator, bool> insert(const pair<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<K, V>, MapKeyOfT> _t;
};
}
使用一个底层的红黑树 _t 来存储键-值对(pair)。以下是这个 map 类的主要特点和功能:
map 类没有在提供的代码中实现构造函数,但可以自行添加。map 类定义了一个嵌套的 iterator 类型,这个迭代器是红黑树中的迭代器。begin() 函数返回红黑树中最小元素的迭代器,而 end() 函数返回表示结束位置的迭代器。这使得你可以使用迭代器来遍历 map 中的键-值对。insert 函数用于向 map 中插入键-值对。它通过调用底层红黑树 _t 的 Insert 函数来执行插入操作,并返回一个 pair 对象,其中包含一个迭代器和一个布尔值。迭代器指向插入的元素,布尔值表示是否插入成功。如果插入的键已经存在,则返回的布尔值为 false,否则为 true。operator[] 函数用于通过键访问 map 中的值。它首先调用 insert 函数尝试插入键-值对,然后返回插入的元素的值的引用。如果键已经存在,则不会插入新元素,而是返回已存在元素的引用。void test_map()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countMap;
for (auto& str : arr)
{
// 1、str不在countMap中,插入pair(str, int()),然后在对返回次数++
// 2、str在countMap中,返回value(次数)的引用,次数++;
countMap[str]++;
}
map<string, int>::iterator it = countMap.begin();
while (it != countMap.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
}
输出结果
苹果:6
西瓜:3
香蕉:2
苹果:6
西瓜:3
香蕉:2
#pragma once
#include "RBTree.hpp"
namespace yulao
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
pair<iterator, bool> insert(const K& key)
{
return _t.Insert(key);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
set 类没有在提供的代码中实现构造函数,但可以自行添加。set 类定义了一个嵌套的 iterator 类型,这个迭代器是红黑树中的迭代器。begin() 函数返回红黑树中最小元素的迭代器,而 end() 函数返回表示结束位置的迭代器。这使得你可以使用迭代器来遍历 set 中的元素。insert 函数用于向 set 中插入元素。它通过调用底层红黑树 _t 的 Insert 函数来执行插入操作,并返回一个 pair 对象,其中包含一个迭代器和一个布尔值。迭代器指向插入的元素,布尔值表示是否插入成功。如果插入的元素已经存在,则返回的布尔值为 false,否则为 true。void test_set()
{
set<int> s;
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
s.insert(3);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(3);
s.insert(6);
s.insert(4);
s.insert(9);
s.insert(7);
it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
输出结果
1 2 3 4 5 6 7 9
这里我们只是通过我们之前学习的红黑树的基础上做了一些模板上的修改,简单的实现了map和set的迭代器功能和插入功能,但这种使用红黑树底层模拟 map 和 set 的迭代器和插入功能的实现有以下作用和好处:
map 和 set,你可以更灵活地定义自己的数据结构,而不仅限于使用标准库提供的容器。这使得你可以根据特定的需求来设计和实现数据结构,以满足项目的要求。