C++ STL 容器使用指南

全面掌握C++标准模板库中的容器类型,了解其特点、适用场景和最佳实践

STL容器概览

STL容器是用于存储和管理数据集合的模板类,提供了数据组织、访问和操作的统一接口。C++标准库提供了多种容器类型,每种针对特定使用场景进行了优化。

容器分类

序列容器
4种

vector, list, deque, array

关联容器
4种

set, multiset, map, multimap

无序容器
4种

unordered_set, unordered_multiset, unordered_map, unordered_multimap

容器适配器
3种

stack, queue, priority_queue

选择容器的关键因素

容器基本使用模式
#include <iostream>
#include <vector>
#include <set>
#include <unordered_map>

int main() {
    // 序列容器 - vector
    std::vector<int> vec = {1, 2, 3};
    vec.push_back(4);
    std::cout << "Vector: ";
    for (int n : vec) std::cout << n << " ";
    
    // 关联容器 - set
    std::set<int> s = {3, 1, 4, 1, 5};
    std::cout << "\nSet: ";
    for (int n : s) std::cout << n << " ";
    
    // 无序容器 - unordered_map
    std::unordered_map<std::string, int> ages;
    ages["Alice"] = 30;
    ages["Bob"] = 25;
    std::cout << "\nAlice's age: " << ages["Alice"];
    
    return 0;
}

序列容器

序列容器按线性顺序存储元素,保留了元素的插入顺序。

vector
序列容器
动态数组

动态数组,在内存中连续存储元素。支持快速随机访问,尾部插入/删除高效,但中间插入/删除较慢。

随机访问
O(1)
尾部插入/删除
平均 O(1)
中间插入/删除
O(n)
内存占用
vector 使用示例
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    // 创建vector
    std::vector<int> nums = {3, 1, 4, 1, 5, 9};
    
    // 添加元素
    nums.push_back(2);
    nums.insert(nums.begin() + 2, 7); // 在索引2处插入
    
    // 排序
    std::sort(nums.begin(), nums.end());
    
    // 遍历
    std::cout << "Vector: ";
    for (int n : nums) std::cout << n << " ";
    
    // 容量操作
    std::cout << "\nSize: " << nums.size();
    std::cout << "\nCapacity: " << nums.capacity();
    
    // 删除元素
    nums.pop_back();
    nums.erase(nums.begin() + 3);
    
    return 0;
}
list
序列容器
双向链表

双向链表实现。在任何位置插入/删除高效,但不支持随机访问。内存占用较高(每个元素需要额外指针空间)。

插入/删除
O(1)
随机访问
O(n)
内存占用
缓存友好
list 使用示例
#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {1, 2, 3};
    
    // 添加元素
    lst.push_front(0);  // 头部插入
    lst.push_back(4);   // 尾部插入
    
    // 中间插入
    auto it = lst.begin();
    std::advance(it, 2);
    lst.insert(it, 10);
    
    // 删除元素
    lst.pop_front();
    lst.remove(3); // 删除所有值为3的元素
    
    // 遍历
    std::cout << "List: ";
    for (int n : lst) std::cout << n << " ";
    
    // 排序
    lst.sort();
    
    // 合并
    std::list<int> lst2 = {5, 6, 7};
    lst.merge(lst2);
    
    return 0;
}
deque
序列容器
双端队列

双端队列,支持在头部和尾部高效插入/删除。由多个固定大小的数组块组成,不是完全连续存储。

头部/尾部操作
O(1)
随机访问
O(1)
中间插入/删除
O(n)
内存占用
deque 使用示例
#include <iostream>
#include <deque>

int main() {
    std::deque<int> dq = {2, 3, 4};
    
    // 双端操作
    dq.push_front(1);
    dq.push_back(5);
    
    // 访问元素
    std::cout << "Front: " << dq.front();
    std::cout << "\nBack: " << dq.back();
    std::cout << "\nElement at index 2: " << dq[2];
    
    // 中间插入
    dq.insert(dq.begin() + 2, 10);
    
    // 删除
    dq.pop_front();
    dq.pop_back();
    
    return 0;
}

关联容器

关联容器基于键自动排序元素,通常使用红黑树实现。

set
关联容器
有序集合

存储唯一键的有序集合。自动排序,不允许重复元素。

查找
O(log n)
插入/删除
O(log n)
内存占用
顺序遍历
有序
set 使用示例
#include <iostream>
#include <set>

int main() {
    std::set<int> s;
    
    // 插入元素
    s.insert(3);
    s.insert(1);
    s.insert(4);
    s.insert(1); // 重复元素,不会被插入
    
    // 遍历(自动排序)
    std::cout << "Set: ";
    for (int n : s) std::cout << n << " "; // 1 3 4
    
    // 查找
    auto it = s.find(3);
    if (it != s.end()) {
        std::cout << "\nFound 3";
    }
    
    // 删除
    s.erase(3);
    
    return 0;
}
map
关联容器
键值对映射

存储键值对的有序映射。键唯一,自动按键排序。

查找
O(log n)
插入/删除
O(log n)
内存占用
键唯一
map 使用示例
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> ages;
    
    // 插入元素
    ages["Alice"] = 30;
    ages["Bob"] = 25;
    ages["Charlie"] = 35;
    
    // 插入方式2
    ages.insert(std::make_pair("David", 28));
    
    // 遍历(按键排序)
    std::cout << "Ages:\n";
    for (const auto& pair : ages) {
        std::cout << pair.first << ": " << pair.second << "\n";
    }
    
    // 查找
    auto it = ages.find("Bob");
    if (it != ages.end()) {
        std::cout << "Bob's age: " << it->second;
    }
    
    // 删除
    ages.erase("Alice");
    
    return 0;
}

无序容器

无序容器使用哈希表实现,提供平均常数时间复杂度的操作。

unordered_set
无序容器
哈希集合

基于哈希表的集合实现,元素无序存储,不允许重复。

平均查找
O(1)
最差查找
O(n)
插入/删除
平均 O(1)
内存占用
unordered_set 使用示例
#include <iostream>
#include <unordered_set>
#include <string>

int main() {
    std::unordered_set<std::string> us;
    
    // 插入元素
    us.insert("apple");
    us.insert("banana");
    us.insert("orange");
    us.insert("apple"); // 重复,不会被插入
    
    // 遍历(无序)
    std::cout << "Fruits: ";
    for (const auto& fruit : us) {
        std::cout << fruit << " ";
    }
    
    // 查找
    if (us.find("banana") != us.end()) {
        std::cout << "\nFound banana";
    }
    
    // 桶接口
    std::cout << "\nBucket count: " << us.bucket_count();
    std::cout << "\nLoad factor: " << us.load_factor();
    
    return 0;
}
unordered_map
无序容器
哈希映射

基于哈希表的键值对映射,元素无序存储,键唯一。

平均查找
O(1)
插入/删除
平均 O(1)
内存占用
顺序遍历
无序
unordered_map 使用示例
#include <iostream>
#include <unordered_map>
#include <string>

int main() {
    std::unordered_map<std::string, int> wordCount;
    
    // 插入元素
    wordCount["apple"] = 5;
    wordCount["banana"] = 3;
    wordCount["orange"] = 2;
    
    // 插入方式2
    wordCount.insert({"grape", 4});
    
    // 更新值
    wordCount["apple"]++;
    
    // 遍历
    std::cout << "Word counts:\n";
    for (const auto& pair : wordCount) {
        std::cout << pair.first << ": " << pair.second << "\n";
    }
    
    // 查找
    if (wordCount.find("banana") != wordCount.end()) {
        std::cout << "Banana count: " << wordCount["banana"];
    }
    
    // 桶操作
    std::cout << "\nBucket for 'apple': " << wordCount.bucket("apple");
    
    return 0;
}

容器适配器

容器适配器提供特定数据结构的接口,基于其他容器实现。

stack
容器适配器
LIFO

后进先出(LIFO)数据结构,默认基于deque实现。

push/pop
O(1)
访问顶部
O(1)
随机访问
不支持
stack 使用示例
#include <iostream>
#include <stack>

int main() {
    std::stack<int> stk;
    
    // 压入元素
    stk.push(10);
    stk.push(20);
    stk.push(30);
    
    std::cout << "Stack size: " << stk.size() << "\n";
    std::cout << "Top element: " << stk.top() << "\n";
    
    // 弹出元素
    stk.pop();
    std::cout << "After pop, top element: " << stk.top() << "\n";
    
    // 检查是否为空
    while (!stk.empty()) {
        std::cout << "Popping: " << stk.top() << "\n";
        stk.pop();
    }
    
    return 0;
}
queue
容器适配器
FIFO

先进先出(FIFO)数据结构,默认基于deque实现。

入队/出队
O(1)
访问队首/队尾
O(1)
随机访问
不支持
queue 使用示例
#include <iostream>
#include <queue>

int main() {
    std::queue<std::string> q;
    
    // 入队
    q.push("Alice");
    q.push("Bob");
    q.push("Charlie");
    
    std::cout << "Queue size: " << q.size() << "\n";
    std::cout << "Front: " << q.front() << "\n";
    std::cout << "Back: " << q.back() << "\n";
    
    // 出队
    q.pop();
    std::cout << "After pop, front: " << q.front() << "\n";
    
    // 遍历队列
    std::cout << "Queue elements: ";
    while (!q.empty()) {
        std::cout << q.front() << " ";
        q.pop();
    }
    
    return 0;
}
priority_queue
容器适配器
优先级队列

优先级队列,元素按优先级排序,默认基于vector实现(大顶堆)。

插入元素
O(log n)
访问顶部
O(1)
删除顶部
O(log n)
priority_queue 使用示例
#include <iostream>
#include <queue>
#include <vector>
#include <functional> // 用于greater

int main() {
    // 最大堆(默认)
    std::priority_queue<int> maxHeap;
    
    maxHeap.push(30);
    maxHeap.push(10);
    maxHeap.push(50);
    maxHeap.push(20);
    
    std::cout << "Max Heap: ";
    while (!maxHeap.empty()) {
        std::cout << maxHeap.top() << " ";
        maxHeap.pop();
    }
    
    // 最小堆
    std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
    
    minHeap.push(30);
    minHeap.push(10);
    minHeap.push(50);
    minHeap.push(20);
    
    std::cout << "\nMin Heap: ";
    while (!minHeap.empty()) {
        std::cout << minHeap.top() << " ";
        minHeap.pop();
    }
    
    // 自定义优先级
    auto cmp = [](int left, int right) { 
        return (left % 10) > (right % 10); // 按个位数从大到小
    };
    std::priority_queue<int, std::vector<int>, decltype(cmp)> customHeap(cmp);
    
    customHeap.push(25);
    customHeap.push(17);
    customHeap.push(36);
    customHeap.push(42);
    
    std::cout << "\nCustom Heap: ";
    while (!customHeap.empty()) {
        std::cout << customHeap.top() << " ";
        customHeap.pop();
    }
    
    return 0;
}

容器比较

不同容器在不同场景下有各自的优势,选择合适的容器可以显著提高程序性能。

容器特性对比表

容器 内部结构 随机访问 插入/删除效率 内存占用 元素顺序 重复元素 最佳使用场景
vector 动态数组 O(1) 尾部: O(1)
中间: O(n)
插入顺序 允许 随机访问频繁,尾部操作多
list 双向链表 O(n) 任意位置 O(1) 插入顺序 允许 频繁在任意位置插入/删除
deque 分块数组 O(1) 头尾: O(1)
中间: O(n)
插入顺序 允许 双端队列操作
set 红黑树 不支持 O(log n) 排序顺序 不允许 有序唯一元素集合
map 红黑树 不支持 O(log n) 按键排序 键唯一 键值对映射,需有序
unordered_set 哈希表 不支持 平均: O(1)
最差: O(n)
无序 不允许 快速查找,不关心顺序
unordered_map 哈希表 不支持 平均: O(1)
最差: O(n)
无序 键唯一 键值对快速查找
stack 适配器(deque) 不支持 顶部 O(1) LIFO 允许 后进先出场景
queue 适配器(deque) 不支持 尾部入队 O(1)
头部出队 O(1)
FIFO 允许 先进先出场景
priority_queue 适配器(vector) 不支持 插入: O(log n)
删除: O(log n)
优先级顺序 允许 优先级调度,堆操作

时间复杂度对比

操作 vector list deque set/map unordered_set/map
随机访问 O(1) O(n) O(1) O(n) O(n)
头部插入 O(n) O(1) O(1) O(log n) 平均 O(1)
尾部插入 平均 O(1) O(1) O(1) O(log n) 平均 O(1)
中间插入 O(n) O(1) O(n) O(log n) 平均 O(1)
查找元素 O(n) O(n) O(n) O(log n) 平均 O(1)
删除元素 O(n) O(1) O(n) O(log n) 平均 O(1)

容器选择指南

需要快速查找

  • 使用set/map(需要排序)
  • 使用unordered_set/map(无需排序)
  • 避免使用list进行查找

需要随机访问

  • 使用vectordeque
  • 避免使用listsetmap

频繁插入/删除

  • 头部/尾部:使用dequelist
  • 中间位置:使用list
  • 避免使用vector在中间插入

内存敏感

  • 使用vector(连续内存)
  • 避免使用list(额外指针开销)