在C++标准模板库中,set和map作为关联式容器的代表,其底层实现基于红黑树这种高效的自平衡二叉搜索树。理解它们的实现机制,对于掌握STL设计哲学至关重要。红黑树通过以下规则保持平衡:
这种结构保证了最坏情况下O(log n)的查找效率,相比哈希表的O(1)虽然略逊,但提供了稳定的性能表现和有序数据存储特性。
cpp复制enum Color { RED, BLACK };
template<class T>
struct RBTreeNode {
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Color _color;
RBTreeNode(const T& data = T())
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _color(RED) // 新节点默认红色
{}
};
节点设计要点:
插入新节点后的调整分为五种情况处理:
关键技巧:旋转操作前先确定支点方向,保持二叉搜索树性质不变
cpp复制template<class K>
class set {
public:
typedef RBTreeNode<K> Node;
// 迭代器相关
class iterator;
iterator begin();
iterator end();
// 容量操作
bool empty() const;
size_t size() const;
// 修改操作
pair<iterator, bool> insert(const K& key);
size_t erase(const K& key);
// 查找操作
iterator find(const K& key);
size_t count(const K& key);
private:
Node* _root = nullptr;
};
cpp复制pair<iterator, bool> insert(const K& key) {
if (_root == nullptr) {
_root = new Node(key);
_root->_color = BLACK;
return make_pair(iterator(_root), true);
}
Node* parent = nullptr;
Node* cur = _root;
while (cur) {
if (cur->_data < key) {
parent = cur;
cur = cur->_right;
}
else if (key < cur->_data) {
parent = cur;
cur = cur->_left;
}
else {
return make_pair(iterator(cur), false);
}
}
cur = new Node(key);
if (parent->_data < key) {
parent->_right = cur;
}
else {
parent->_left = cur;
}
cur->_parent = parent;
// 红黑树平衡调整
while (parent && parent->_color == RED) {
Node* grand = parent->_parent;
if (parent == grand->_left) {
Node* uncle = grand->_right;
// Case处理...
}
// 对称处理右子树情况...
}
_root->_color = BLACK;
return make_pair(iterator(cur), true);
}
cpp复制template<class K, class V>
class map {
public:
typedef pair<const K, V> value_type;
typedef RBTreeNode<value_type> Node;
// 重载operator[]实现
V& operator[](const K& key) {
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
// 其他接口与set类似...
};
cpp复制struct MapKeyOfT {
const K& operator()(const pair<const K, V>& kv) {
return kv.first;
}
};
// 在红黑树内部使用比较器
template<class K, class V>
class RBTree {
// ...
bool operator()(const K& k1, const K& k2) {
return k1 < k2;
}
};
cpp复制class iterator {
public:
typedef RBTreeNode<T> Node;
iterator(Node* node = nullptr) : _node(node) {}
T& operator*() { return _node->_data; }
iterator& operator++() {
if (_node->_right) {
// 找右子树的最左节点
_node = _node->_right;
while (_node->_left) {
_node = _node->_left;
}
}
else {
// 向上回溯
Node* parent = _node->_parent;
while (parent && _node == parent->_right) {
_node = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
private:
Node* _node;
};
通过模板参数区分const迭代器:
cpp复制template<class T, class Ref, class Ptr>
class __TreeIterator {
// 实现细节...
};
typedef __TreeIterator<T, T&, T*> iterator;
typedef __TreeIterator<T, const T&, const T*> const_iterator;
cpp复制class NodeAllocator {
public:
static Node* Allocate() {
if (_freeList == nullptr) {
return static_cast<Node*>(::operator new(sizeof(Node)));
}
Node* obj = _freeList;
_freeList = *(Node**)_freeList;
return obj;
}
static void Deallocate(Node* p) {
*(Node**)p = _freeList;
_freeList = p;
}
private:
static Node* _freeList;
};
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 迭代器失效 | 树结构调整未更新迭代器 | 检查erase后是否继续使用旧迭代器 |
| 内存泄漏 | 节点删除未释放内存 | 实现析构函数递归释放 |
| 排序异常 | 比较器不符合严格弱序 | 重载operator<保证全序关系 |
| 崩溃于end()++ | 未处理边界条件 | 对end()迭代器特殊处理 |
cpp复制bool CheckRBTree() {
if (_root == nullptr) return true;
if (_root->_color == RED) return false;
// 计算任意路径黑节点数
int blackCount = 0;
Node* cur = _root;
while (cur) {
if (cur->_color == BLACK)
++blackCount;
cur = cur->_left;
}
return _CheckRBTree(_root, blackCount, 0);
}
bool _CheckRBTree(Node* root, int blackCount, int pathBlack) {
if (root == nullptr) {
return blackCount == pathBlack;
}
if (root->_color == BLACK) {
++pathBlack;
}
else {
if ((root->_left && root->_left->_color == RED) ||
(root->_right && root->_right->_color == RED))
return false;
}
return _CheckRBTree(root->_left, blackCount, pathBlack) &&
_CheckRBTree(root->_right, blackCount, pathBlack);
}
cpp复制void Benchmark() {
std::set<int> stdSet;
mySet<int> mySet;
// 插入测试
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000000; ++i) {
stdSet.insert(rand());
}
auto end = std::chrono::high_resolution_clock::now();
// 输出耗时...
// 查找测试...
// 删除测试...
}
异常安全保证:
调试支持:
cpp复制void PrintTree(Node* root, int indent = 0) {
if (root == nullptr) return;
PrintTree(root->_right, indent + 4);
std::cout << std::string(indent, ' ');
std::cout << root->_data << (root->_color ? "(B)" : "(R)") << "\n";
PrintTree(root->_left, indent + 4);
}
兼容性处理:
在实际项目中,建议优先使用标准库实现。自制容器的主要价值在于理解底层机制,当需要特殊优化时(如定制内存分配策略),才考虑自行实现关键组件。