首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >从零带你封装 C++ STL 的 map 和 set ——红黑树底层实现全解析

从零带你封装 C++ STL 的 map 和 set ——红黑树底层实现全解析

作者头像
用户11289931
发布于 2025-05-29 00:45:39
发布于 2025-05-29 00:45:39
24700
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

写在前面

嘿,程序员们!👋 今天我们要揭秘C++中最牛的数据结构之一——红黑树!相信很多小伙伴对map和set的内部实现一直很好奇,今天我们就一起"解剖"它们!💪

🎯 这篇文章能帮你:

  • 彻底搞懂红黑树的神秘面纱
  • 了解map和set的内部乾坤
  • 手写一个迷你版STL容器(装逼利器!)

第一章:红黑树究竟是啥?

1.1 红黑树定义(通俗易懂版)

想象红黑树就像是一个有严格纪律的家族:

  1. 族长(根节点)必须是黑色的
  2. 所有的"旁系"(叶子节点)都是黑色
  3. 红色成员不能生红色后代
  4. 每个分支的黑色成员数量必须一致
1.2 节点结构大揭秘
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 颜色枚举,红黑树的"身份证"
enum Color { 
    RED,    // 红色成员
    BLACK   // 黑色成员
};

// 节点:红黑树的基本单元
template <typename T>
struct RBTreeNode {
    T data;               // 数据
    Color color;          // 颜色标记
    RBTreeNode* parent;   // 父节点指针
    RBTreeNode* left;     // 左孩子
    RBTreeNode* right;    // 右孩子

    // 新成员默认是红色的
    RBTreeNode(const T& value) : 
        data(value), 
        color(RED), 
        parent(nullptr), 
        left(nullptr), 
        right(nullptr) {}
};
1.3 左旋:节点的"换位大法"
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 左旋:想象成一个节点向左侧"倒下"
void rotateLeft(RBTreeNode* x) {
    RBTreeNode* y = x->right;  // 找到要"顶替"的节点
    x->right = y->left;         // 调整关系
    
    // 一系列复杂的指针重连接...
    if (y->left != nullptr)
        y->left->parent = x;
    
    // 更新根节点或父节点的子节点
    if (x->parent == nullptr)
        root = y;
    else if (x == x->parent->left)
        x->parent->left = y;
    else
        x->parent->right = y;
    
    y->left = x;
    x->parent = y;
}

第二章:map的内部乾坤

2.1 map是个啥?

map就像是一个高级的"翻译官":

  • 存储键值对
  • 自动排序
  • 键值唯一
  • 底层是红黑树
2.2 map的模板实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename Key, typename Value>
class MyMap {
private:
    RBTree<std::pair<Key, Value>> tree;

public:
    // 插入操作
    void insert(const std::pair<Key, Value>& kvPair) {
        tree.insertUnique(kvPair);
    }

    // 牛逼的下标访问
    Value& operator[](const Key& key) {
        // 如果没有就自动创建
        return tree.findOrInsert(key);
    }
};

第三章:性能大比拼

时间复杂度

  • 尽量使用emplace替代insert
  • 对于小型数据,考虑使用unordered_map/unordered_set
  • 避免频繁拷贝大型对象

容器

插入

查找

删除

map

O(log n)

O(log n)

O(log n)

set

O(log n)

O(log n)

O(log n)

操作

平均时间复杂度

插入

O(log n)

查找

O(log n)

删除

O(log n)

空间复杂度
  • 每个节点存储额外颜色信息
  • 总体空间复杂度:O(n)

💡 干货小贴士

  1. 红黑树是平衡二叉树的王者
  2. map和set的高效源于红黑树
  3. 深入源码,提升代码功力

在C++的世界里,mapset绝对是最常用且最强大的容器之一。它们不仅仅是简单的数据结构,更是算法和数据管理的利器。本文将带你全方位解析这两个容器的方方面面。

第四章:迭代器的前世今生

4.1 迭代器是什么?

迭代器可以理解为容器和算法之间的桥梁。它就像一个"智能指针",能够遍历容器中的元素,同时屏蔽不同容器的内部细节。

4.2 迭代器的基本分类

C++中迭代器主要分为五类:

  1. 输入迭代器
  2. 输出迭代器
  3. 前向迭代器
  4. 双向迭代器
  5. 随机访问迭代器
4.3 迭代器的实现原理
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename T>
class MyIterator {
private:
    T* ptr;  // 底层指针

public:
    // 构造函数
    MyIterator(T* p) : ptr(p) {}

    // 解引用操作符
    T& operator*() {
        return *ptr;
    }

    // 前置++操作符
    MyIterator& operator++() {
        ++ptr;
        return *this;
    }

    // 比较操作符
    bool operator!=(const MyIterator& other) {
        return ptr != other.ptr;
    }
};

第五章:map的深度剖析

5.1 map的基本概念

map是一个关联容器,它存储键值对(key-value),并且:

  • 根据键自动排序
  • 键的唯一性
  • 底层基于红黑树实现
5.2 map的典型使用场景
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 词频统计
std::map<std::string, int> wordCount;

// 添加元素
wordCount["hello"] = 10;
wordCount["world"] = 20;

// 遍历
for (const auto& pair : wordCount) {
    std::cout << pair.first << ": " << pair.second << std::endl;
}
5.3 map的内部实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename Key, typename Value>
class MyMap {
private:
    // 内部红黑树
    RBTree<std::pair<const Key, Value>> tree;

public:
    // 插入操作
    void insert(const std::pair<Key, Value>& kvPair) {
        tree.insertUnique(kvPair);
    }

    // 下标访问操作符
    Value& operator[](const Key& key) {
        // 如果键不存在,自动创建
        return tree.findOrInsert(key);
    }

    // 迭代器支持
    auto begin() { return tree.begin(); }
    auto end() { return tree.end(); }
};

第六章:set的实现原理

6.1 set的基本特征

set是只存储键的关联容器:

  • 元素唯一
  • 自动排序
  • 不允许直接修改元素
6.2 set的典型使用场景
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 去重
std::set<int> uniqueNumbers;
uniqueNumbers.insert(1);
uniqueNumbers.insert(2);
uniqueNumbers.insert(1);  // 不会重复插入

// 查找
auto it = uniqueNumbers.find(2);
if (it != uniqueNumbers.end()) {
    std::cout << "找到元素" << std::endl;
}

第七章:实际应用场景

7.1 词频统计
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
std::map<std::string, int> wordFrequency;

void countWords(const std::string& text) {
    std::istringstream iss(text);
    std::string word;
    
    while (iss >> word) {
        wordFrequency[word]++;
    }
}
7.2 缓存实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename Key, typename Value>
class LRUCache {
private:
    std::map<Key, Value> cache;
    std::list<Key> lruList;
    size_t capacity;

public:
    void put(const Key& key, const Value& value) {
        if (cache.size() >= capacity) {
            // 移除最近最少使用的元素
            cache.erase(lruList.front());
            lruList.pop_front();
        }
        
        cache[key] = value;
        lruList.push_back(key);
    }
};

结语

红黑树就是这么牛!🐂

map和set不仅仅是容器,更是C++中的艺术品。深入理解它们,将极大提升你的编程能力!希望通过这篇文章,你能窥探C++容器的神秘面纱!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
    • 🎯 这篇文章能帮你:
  • 第一章:红黑树究竟是啥?
    • 1.1 红黑树定义(通俗易懂版)
    • 1.2 节点结构大揭秘
    • 1.3 左旋:节点的"换位大法"
  • 第二章:map的内部乾坤
    • 2.1 map是个啥?
    • 2.2 map的模板实现
  • 第三章:性能大比拼
    • 时间复杂度
    • 空间复杂度
  • 💡 干货小贴士
  • 第四章:迭代器的前世今生
    • 4.1 迭代器是什么?
    • 4.2 迭代器的基本分类
    • 4.3 迭代器的实现原理
  • 第五章:map的深度剖析
    • 5.1 map的基本概念
    • 5.2 map的典型使用场景
    • 5.3 map的内部实现
  • 第六章:set的实现原理
    • 6.1 set的基本特征
    • 6.2 set的典型使用场景
  • 第七章:实际应用场景
    • 7.1 词频统计
    • 7.2 缓存实现
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档