C++容器底层数据结构介绍

发布时间:2021-06-29 09:56:04 作者:chen
来源:亿速云 阅读:387
# C++容器底层数据结构介绍

## 前言

C++标准库(STL)提供了多种容器类型,每种容器都有其特定的应用场景和性能特征。了解这些容器的底层数据结构对于编写高效代码至关重要。本文将深入探讨vector、list、deque、set/multiset、map/multimap等常用容器的底层实现机制。

## 1. 序列式容器

### 1.1 vector - 动态数组

**底层数据结构**:连续内存分配的动态数组

```cpp
template <class T, class Alloc = allocator<T>>
class vector {
    T* _M_start;        // 指向数组首元素
    T* _M_finish;       // 指向最后一个元素的下一个位置
    T* _M_end_of_storage; // 指向分配内存的末尾
};

关键特性: - 内存连续分配,支持随机访问(O(1)) - 扩容机制:当size==capacity时,通常按2倍或1.5倍扩容 - 插入/删除尾部元素O(1),中间元素O(n)

典型实现

void push_back(const T& value) {
    if (_M_finish != _M_end_of_storage) {
        construct(_M_finish, value);
        ++_M_finish;
    } else {
        reallocate_insert(end(), value);
    }
}

1.2 list - 双向链表

底层数据结构:双向循环链表

struct _List_node {
    _List_node* _M_next;
    _List_node* _M_prev;
    T _M_data;
};

template <class T>
class list {
    _List_node<T>* _M_node;  // 指向哨兵节点
};

关键特性: - 非连续内存存储,每个元素单独分配 - 插入/删除操作O(1),不支持随机访问 - 迭代器失效规则:只有被删除元素的迭代器会失效

1.3 deque - 双端队列

底层数据结构:分段连续存储的”中央控制器+多缓冲区”

template <class T>
class deque {
    T** _M_map;         // 指向指针数组(中央控制器)
    size_t _M_map_size;  // 中央控制器大小
    iterator _M_start;   // 起始迭代器
    iterator _M_finish;  // 结束迭代器
};

典型缓冲区结构

中央控制器
+---+---+---+---+
| * | * | * | * |
+---+---+---+---+
  |   |   |   |
  v   v   v   v
 [x,x][x,x,x][][x,x,x]

关键特性: - 头尾插入O(1),中间插入O(n) - 伪连续存储,迭代器比vector复杂 - 扩容时只需在中央控制器两端添加新指针

2. 关联式容器

2.1 set/multiset - 集合

底层数据结构:红黑树(自平衡二叉搜索树)

struct _Rb_tree_node {
    _Rb_tree_color _M_color;
    _Rb_tree_node* _M_parent;
    _Rb_tree_node* _M_left;
    _Rb_tree_node* _M_right;
    T _M_value_field;
};

template <class Key>
class set {
    _Rb_tree<Key, Key> _M_t;
};

红黑树特性: 1. 每个节点非红即黑 2. 根节点为黑色 3. 红色节点的子节点必须为黑 4. 从任一节点到其叶子的路径包含相同数量黑节点

操作复杂度: - 插入/删除/查找:O(log n) - 自动排序,元素不可修改

2.2 map/multimap - 映射

底层数据结构:同样基于红黑树

template <class Key, class T>
class map {
    typedef pair<const Key, T> value_type;
    _Rb_tree<Key, value_type> _M_t;
};

与set的区别: - 存储的是key-value对 - 通过key排序和查找 - 提供operator[]访问接口

3. 无序关联式容器(C++11)

3.1 unordered_set/unordered_multiset

底层数据结构:哈希表(数组+链表/红黑树)

template <class Key>
class unordered_set {
    std::vector<std::forward_list<Key>> _M_buckets;
    size_t _M_bucket_count;
    float _M_max_load_factor;
};

关键特性: - 平均查找复杂度O(1),最坏O(n) - 负载因子 = 元素数量/桶数量 - 当负载因子超过阈值时触发rehash

3.2 unordered_map/unordered_multimap

底层数据结构:与unordered_set类似,存储pair

template <class Key, class T>
class unordered_map {
    std::vector<std::forward_list<std::pair<const Key, T>>> _M_buckets;
};

优化技巧: - 自定义哈希函数减少冲突 - 预分配足够桶数量避免rehash - 对于小对象,开放寻址法可能性能更好

4. 容器适配器

4.1 stack - 栈

底层默认容器:deque

template <class T, class Container = deque<T>>
class stack {
    Container c;
};

可选底层:也可用vector或list实现

4.2 queue - 队列

底层默认容器:deque

template <class T, class Container = deque<T>>
class queue {
    Container c;
};

特性: - 不能用vector实现(无pop_front) - list实现效率较低

4.3 priority_queue - 优先队列

底层数据结构:vector + 堆算法

template <class T, class Container = vector<T>, 
          class Compare = less<typename Container::value_type>>
class priority_queue {
    Container c;
    Compare comp;
};

堆操作: - push: O(log n) - 上浮操作 - pop: O(log n) - 下沉操作 - top: O(1) - 返回根元素

5. 底层数据结构对比

容器 底层结构 访问 插入/删除 内存 迭代器失效条件
vector 动态数组 O(1) 尾部O(1),中间O(n) 连续 扩容后全部失效
list 双向链表 O(n) O(1) 不连续 仅删除元素失效
deque 分块数组 O(1) 头尾O(1),中间O(n) 伪连续 插入可能导致全部失效
set/map 红黑树 O(log n) O(log n) 不连续 仅删除元素失效
unordered_set/map 哈希表 平均O(1) 平均O(1) 不连续 rehash后全部失效

6. 性能优化建议

  1. vector使用技巧

    • 预分配足够空间(reserve)
    • 避免在中间位置插入
    • 小对象优先考虑
  2. list适用场景

    • 频繁在任意位置插入删除
    • 不需要随机访问
    • 大对象存储
  3. 关联容器选择

    • 需要有序访问 → set/map
    • 追求查找速度 → unordered_set/map
    • 内存敏感 → 考虑flat_map(C++23)
  4. 哈希表优化

    • 设计良好的哈希函数
    • 调整合适的负载因子
    • 对于已知元素数量,预分配桶

7. C++17/C++20新特性

  1. 节点操作(C++17)

    std::map<int, string> m1, m2;
    auto node = m1.extract(10);  // O(log n)
    if (!node.empty())
       m2.insert(std::move(node));
    
  2. try_emplace/insert_or_assign

    std::map<int, Resource> m;
    m.try_emplace(10, "test");  // 不存在时才构造
    
  3. 连续内存容器(C++20)

    • std::span - 非拥有视图
    • std::to_array - 原生数组转换

结语

理解C++容器的底层数据结构可以帮助开发者: 1. 根据场景选择最合适的容器 2. 避免性能陷阱(如vector的频繁扩容) 3. 正确处理迭代器失效问题 4. 编写更高效的内存访问代码

实际开发中应结合具体需求(访问模式、数据规模、内存限制等)进行容器选择,必要时可通过性能测试验证选择。


本文约3150字,详细介绍了C++主要容器的底层实现机制,包括数据结构选择、操作复杂度分析和实际使用建议。通过理解这些底层原理,开发者可以更高效地使用STL容器。 “`

推荐阅读:
  1. Redis专题(2):Redis数据结构底层探秘
  2. docker底层原理介绍

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c++

上一篇:jQuery怎么实现拖拽排序效果

下一篇:Synchronized的原理是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》