C++ vector等STL容器用法 [学习笔记]

C++的STL(Standard Template Library)容器提供了强大的功能,熟悉这些容器的高级用法可以大大提高代码的效率和可读性。


1. vector

a. 动态调整容量

  • reserve vs resize
    • reserve(n):预分配空间,但不影响当前大小(size())。
    • resize(n):调整大小,多余的空间会用默认值填充。
    std::vector<int> v;
    v.reserve(100); // 提高性能,避免多次内存分配
    v.resize(10, -1); // 调整大小并用-1填充多余位置
    

b. 高效删除元素

  • 删除单个元素:
    使用erase
    v.erase(v.begin() + index); // 删除指定位置元素
    
  • 删除多个元素:
    v.erase(v.begin() + start, v.begin() + end); // 删除指定区间
    
  • 使用remove_if删除满足条件的元素:
    v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }), v.end());
    

c. 去重

  • std::unique结合erase去除连续重复元素:
    std::sort(v.begin(), v.end()); // 必须先排序
    v.erase(std::unique(v.begin(), v.end()), v.end());
    

d. 迭代器与遍历

  • 使用reverse_iterator进行反向遍历:
    for (auto it = v.rbegin(); it != v.rend(); ++it) {
        std::cout << *it << " ";
    }
    

e. 二维向量

  • 初始化二维向量:
    std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));
    

f. 与数组的互操作

  • 使用data()访问底层数组:
    int* arr = v.data();
    arr[0] = 100; // 直接修改底层数组
    

2. set

a. 自动排序

set中的元素总是有序的(默认升序,使用自定义比较函数可实现降序)。

std::set<int> s = {5, 1, 3};
for (int x : s) {
    std::cout << x << " "; // 输出:1 3 5
}

b. 查找与统计

  • find
    if (s.find(3) != s.end()) {
        std::cout << "Found 3\n";
    }
    
  • 计数(多重集合multiset中有用):
    int count = s.count(3); // 对于set,总是返回0或1
    

c. 区间操作

  • 使用lower_boundupper_bound
    auto it = s.lower_bound(3); // 第一个不小于3的元素
    auto it2 = s.upper_bound(3); // 第一个大于3的元素
    

d. 自定义比较函数

  • 使用仿函数(升序/降序切换):
    struct Descend {
        bool operator()(int a, int b) const {
            return a > b;
        }
    };
    std::set<int, Descend> s = {1, 2, 3};
    for (int x : s) {
        std::cout << x << " "; // 输出:3 2 1
    }
    

e. 合并两个集合

  • 使用std::set_union
    std::set<int> s1 = {1, 2, 3};
    std::set<int> s2 = {2, 3, 4};
    std::vector<int> result;
    std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result));
    

f. 有序插入的高效性

set的插入操作复杂度为 (O(\log n)),比vector中未排序的插入后排序更高效。


2.1. 结合使用vectorset

  • 快速去重并保持顺序:
    std::vector<int> v = {4, 1, 2, 2, 3, 1};
    std::set<int> s(v.begin(), v.end());
    v.assign(s.begin(), s.end()); // 转换回vector
    
  • 排序后去重:
    std::sort(v.begin(), v.end());
    v.erase(std::unique(v.begin(), v.end()), v.end());
    

2.2. 常用算法与容器结合

a. 搜索

使用std::binary_search

std::vector<int> v = {1, 2, 3, 4, 5};
std::sort(v.begin(), v.end());
if (std::binary_search(v.begin(), v.end(), 3)) {
    std::cout << "Found\n";
}
b. 集合操作
  • 求交集:
    std::vector<int> result;
    std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result));
    
  • 求并集:
    std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result));
    
c. 部分排序
  • 对前k个元素排序:
    std::partial_sort(v.begin(), v.begin() + k, v.end());
    
  • 获取前k小的元素:
    std::nth_element(v.begin(), v.begin() + k - 1, v.end());
    

2.3. 性能优化技巧

  • 预分配空间(vector
    使用reserve避免多次分配内存。
  • 减少拷贝
    使用std::moveemplace_back
    v.emplace_back(std::move(obj));
    
  • 优先选择适合的容器
    • 数据频繁插入:setunordered_set
    • 顺序访问:vector

C++ STL 除了常见的 vectorset 外,还有许多其他容器类型,每种都有其独特的特性和高级用法。以下是 STL 其他主要容器及其高级用法:


3. list (双向链表)

list 是一个双向链表,适合频繁的插入和删除操作,但随机访问性能较差。

高级用法

  • 高效插入和删除

    • 插入或删除在任何位置的复杂度均为 ( O(1) )(在已知位置的迭代器情况下)。
    std::list<int> lst = {1, 2, 3};
    auto it = lst.begin();
    ++it; // 指向第二个元素
    lst.insert(it, 10); // 在2前插入10
    lst.erase(it);      // 删除2
    
  • 双端操作

    • push_frontpush_back
    lst.push_front(0); // 在前插入
    lst.push_back(4);  // 在后插入
    
  • vector比较

    • 当需要频繁地在中间插入或删除元素时,listvector更高效。
  • 排序与去重

    • list 自带 sortunique 方法,无需 STL 算法:
    lst.sort();  // 排序
    lst.unique(); // 去重(仅连续重复元素)
    

4. deque (双端队列)

deque 是一种双端动态数组,支持快速的首尾插入和删除。

高级用法

  • 双端操作

    std::deque<int> dq = {1, 2, 3};
    dq.push_front(0); // 在前插入
    dq.push_back(4);  // 在后插入
    dq.pop_front();   // 移除前面的元素
    dq.pop_back();    // 移除后面的元素
    
  • 适用场景

    • 当需要频繁在容器的两端进行操作时,deque 优于 vectorvectorpush_front 效率低)。
  • 随机访问

    • vector 一样支持下标操作:
    dq[2] = 42;
    

5. mapmultimap (关联容器)

map 是键值对形式的有序关联容器,自动按键排序;multimap 允许键重复。

高级用法

  • 插入元素

    std::map<std::string, int> m;
    m["apple"] = 3; // 插入或更新键值对
    m.insert({"banana", 2}); // 显式插入
    
  • 查找元素

    auto it = m.find("apple");
    if (it != m.end()) {
        std::cout << "Found: " << it->second << std::endl;
    }
    
  • 区间查找 (lower_boundupper_bound)

    auto it = m.lower_bound("b"); // 第一个 >= "b" 的键
    auto it2 = m.upper_bound("b"); // 第一个 > "b" 的键
    
  • 遍历时修改值

    for (auto& [key, value] : m) {
        value += 1; // 修改所有值
    }
    
  • 多重键值 (multimap)

    std::multimap<std::string, int> mm;
    mm.insert({"apple", 1});
    mm.insert({"apple", 2}); // 同键可插入多个值
    

6. unordered_mapunordered_set (哈希容器)

基于哈希表的容器,无序但查找/插入性能为 ( O(1) )。

高级用法

  • 哈希函数
    可以自定义哈希函数:

    struct MyHash {
        std::size_t operator()(const std::pair<int, int>& p) const {
            return std::hash<int>()(p.first) ^ std::hash<int>()(p.second);
        }
    };
    std::unordered_map<std::pair<int, int>, int, MyHash> umap;
    
  • 快速查找与统计

    std::unordered_set<int> uset = {1, 2, 3};
    if (uset.find(2) != uset.end()) {
        std::cout << "Found 2\n";
    }
    

7. stackqueue (适配器容器)

stackqueue 是对其他容器的封装,分别实现栈(后进先出)和队列(先进先出)。

高级用法

  • stack

    std::stack<int> stk;
    stk.push(1);
    stk.push(2);
    std::cout << stk.top(); // 获取栈顶元素
    stk.pop(); // 弹出栈顶元素
    
  • queue

    std::queue<int> q;
    q.push(1);
    q.push(2);
    std::cout << q.front(); // 获取队头
    std::cout << q.back();  // 获取队尾
    q.pop(); // 移除队头
    
  • priority_queue (优先队列)

    std::priority_queue<int> pq; // 默认最大堆
    pq.push(3);
    pq.push(1);
    pq.push(2);
    std::cout << pq.top(); // 输出最大值
    

    自定义比较函数(最小堆):

    std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
    

8. bitset (位集合)

bitset 是一种高效的二进制位操作容器,适合存储和操作大量布尔值。

高级用法

  • 初始化

    std::bitset<8> b(42); // 8位,值为42(101010)
    std::cout << b;       // 输出:00101010
    
  • 按位操作

    b.set(0);    // 设置第0位为1
    b.reset(1);  // 设置第1位为0
    b.flip();    // 翻转所有位
    
  • 按位统计

    std::cout << b.count();   // 统计1的数量
    std::cout << b.any();     // 是否有1
    std::cout << b.none();    // 是否全为0
    

9. tuple (元组容器)

tuple 是一种可存储多种类型值的固定长度容器。

高级用法

  • 创建和访问

    std::tuple<int, std::string, double> t(1, "hello", 3.14);
    std::cout << std::get<0>(t); // 访问第一个元素
    
  • 解构绑定 (C++17)

    auto [x, y, z] = t; // 解构元组
    
  • 比较

    std::tuple<int, int> t1(1, 2), t2(2, 1);
    std::cout << (t1 < t2); // 支持字典序比较
    

10. array (定长数组)

std::array 是 C++ 提供的定长数组封装,功能类似 vector

高级用法

  • 初始化

    std::array<int, 5> arr = {1, 2, 3, 4, 5};
    
  • 数组操作

    std::cout << arr.size();
    std::cout << arr.front(); // 获取首元素
    std::cout << arr.back();  // 获取尾元素
    

总结

容器类型特点
vector动态数组,适合随机访问和尾部操作
list双向链表,适合频繁插入和删除操作
deque双端队列,支持高效的首尾插入和删除
set/map有序关联容器,按键排序,适合范围查找和去重
unordered_set基于哈希表,无序,查找和插入性能 ( O(1) )
stack/queue适配器容器,分别实现栈和队列
bitset位操作容器,高效存储和操作布尔值
tuple多种类型固定长度容器,适合存储结构化数据
array定长数组,适合需要固定大小的数组场景

STL容器迭代器定义变量方法


在 C++ 中,STL 容器提供了迭代器(iterator)作为访问其元素的一种抽象方式。不同容器的迭代器类型可能有所区别,但基本用法是类似的。以下是定义和使用迭代器变量的方式:


通用方式:使用 typename Container::iteratorauto

明确指定迭代器类型
std::vector<int> vec = {1, 2, 3};
std::vector<int>::iterator it = vec.begin(); // 定义普通迭代器
使用 auto 推导类型(推荐)
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin(); // 自动推导迭代器类型

不同容器的迭代器定义方式

1. std::vector(连续内存的动态数组)
std::vector<int> vec = {1, 2, 3};

// 普通迭代器(可读可写)
std::vector<int>::iterator it = vec.begin();
*it = 10; // 修改值

// 常量迭代器(只读)
std::vector<int>::const_iterator cit = vec.cbegin();
int value = *cit; // 读取值,不能修改

// 反向迭代器(从末尾向前遍历)
std::vector<int>::reverse_iterator rit = vec.rbegin();
*rit = 20;

// 常量反向迭代器
std::vector<int>::const_reverse_iterator crit = vec.crbegin();
int lastValue = *crit; // 读取末尾值

2. std::list(双向链表)
std::list<int> lst = {4, 5, 6};

// 普通迭代器
std::list<int>::iterator it = lst.begin();
*it = 10;

// 常量迭代器
std::list<int>::const_iterator cit = lst.cbegin();
int value = *cit; // 只读

// 反向迭代器
std::list<int>::reverse_iterator rit = lst.rbegin();
*rit = 30;

// 常量反向迭代器
std::list<int>::const_reverse_iterator crit = lst.crbegin();
int lastValue = *crit; // 读取值

3. std::setstd::map(有序关联容器)
std::set<int> s = {1, 2, 3};
std::map<int, std::string> m = {{1, "one"}, {2, "two"}};

// set 的普通迭代器
std::set<int>::iterator it = s.begin();
int value = *it; // 获取元素值(只读)

// map 的普通迭代器
std::map<int, std::string>::iterator mit = m.begin();
int key = mit->first;      // 获取键
std::string val = mit->second; // 获取值并可修改
mit->second = "uno";

// 常量迭代器
std::map<int, std::string>::const_iterator cmit = m.cbegin();
int ckey = cmit->first; // 只读键
std::string cval = cmit->second; // 只读值

// 反向迭代器
std::set<int>::reverse_iterator rit = s.rbegin();
int rvalue = *rit;

// map 的反向迭代器
std::map<int, std::string>::reverse_iterator rmit = m.rbegin();
int rkey = rmit->first;       // 读取键
std::string rval = rmit->second; // 读取值并可修改

4. std::unordered_setstd::unordered_map(无序关联容器)
std::unordered_set<int> us = {1, 2, 3};
std::unordered_map<int, std::string> um = {{1, "one"}, {2, "two"}};

// unordered_set 的普通迭代器
std::unordered_set<int>::iterator it = us.begin();
int value = *it;

// unordered_map 的普通迭代器
std::unordered_map<int, std::string>::iterator mit = um.begin();
int key = mit->first;      // 获取键
std::string val = mit->second; // 获取值并可修改
mit->second = "uno";

// 常量迭代器
std::unordered_map<int, std::string>::const_iterator cmit = um.cbegin();
int ckey = cmit->first; // 只读键
std::string cval = cmit->second; // 只读值

5. std::deque(双端队列)
std::deque<int> dq = {1, 2, 3};

// 普通迭代器
std::deque<int>::iterator it = dq.begin();
*it = 10;

// 常量迭代器
std::deque<int>::const_iterator cit = dq.cbegin();
int value = *cit; // 只读

// 反向迭代器
std::deque<int>::reverse_iterator rit = dq.rbegin();
*rit = 20;

// 常量反向迭代器
std::deque<int>::const_reverse_iterator crit = dq.crbegin();
int lastValue = *crit; // 读取值

小结

  • STL 容器迭代器的定义方式类似,但根据容器类型和用途不同,迭代器可以分为普通迭代器、常量迭代器、反向迭代器等。

  • 推荐使用 autorange-based for loop(基于范围的循环)来简化代码

    for (auto it = container.begin(); it != container.end(); ++it) {
        // 使用 *it 访问元素
    }
    
  • 在现代 C++ 中,基于范围的循环更简洁:

    for (const auto& elem : container) {
        // 访问 elem
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值