C++ SLT Vector

C++ 中的 std::vector

一、底层结构与内存管理

1. 核心数据结构
  • 连续内存块:元素在内存中连续存储,支持指针算术运算,实现 O(1) 随机访问。
  • 三指针管理(简化模型):
    • _start:指向首个元素的地址(begin() 迭代器)。
    • _finish:指向最后一个有效元素的下一个位置(end() 迭代器),size = _finish - _start
    • _end_of_storage:指向已分配内存的末尾,capacity = _end_of_storage - _start
2. 动态扩容机制
  • size == capacity 时插入元素会触发扩容:
    • 分配新内存:通常扩容至原容量的 2 倍(不同编译器实现可能不同)。
    • 迁移数据:将旧元素拷贝/移动到新内存(涉及构造与析构)。
    • 释放旧内存:更新三指针指向新空间。
  • 均摊时间复杂度push_back() 的均摊复杂度为 O(1),因扩容频率随元素增长降低。
3. 迭代器与失效问题
  • 迭代器类型:随机访问迭代器(支持 +-[] 操作)。
  • 失效场景
    • 扩容后:所有迭代器、指针、引用失效(地址变更)。
    • 中间插入/删除:操作位置之后的迭代器失效。

二、常用函数详解

1. 构造与初始化
方法示例说明
默认构造vector<int> v1;创建空 vector
指定大小和初值vector<int> v2(5, 10);5 个元素,值均为 10
列表初始化vector<int> v3 = {1, 2, 3};C++11 初始化列表
范围构造vector<int> v4(v3.begin(), v3.end());复制另一容器的区间
2. 元素访问
函数示例特性
operator[]int a = v[0];不检查边界,效率高
at()int b = v.at(1);边界检查,越界抛 out_of_range
front()/back()int f = v.front();访问首/尾元素
data()int* p = v.data();返回底层数组指针(C++11)
3. 容量操作
函数说明
size()返回当前元素个数
capacity()返回当前分配的内存容量
reserve(n)预分配内存,避免多次扩容(如 reserve(1000)
shrink_to_fit()释放多余内存(容量降至 size
empty()判断容器是否为空
4. 修改操作
函数示例时间复杂度
push_back(val)v.push_back(10);尾部插入,均摊 O(1)
emplace_back(args)v.emplace_back(10);直接构造,避免拷贝(更高效)
pop_back()v.pop_back();尾部删除,O(1)
insert(pos, val)v.insert(v.begin() + 1, 20);中间插入,O(n)
erase(pos)v.erase(v.begin());中间删除,O(n)
clear()v.clear();清空元素(不释放内存)
resize(n)v.resize(10);调整元素个数
5. 迭代器与遍历
// 1. 下标遍历
for (size_t i = 0; i < v.size(); ++i) { 
    cout << v[i]; 
}

// 2. 迭代器遍历
for (auto it = v.begin(); it != v.end(); ++it) { 
    cout << *it; 
}

// 3. 范围 for 循环(C++11)
for (int num : v) { 
    cout << num; 
}
6. 删除元素
6.1 删除具体位置元素

删除元素:vec.erase(vec.begin() + 2);

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 打印原始向量
    std::cout << "原始向量,当前向量大小: " << vec.size() << std::endl;
    for (int num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    // 移除第三个元素(索引 2,值为 3)
    int removed_value = vec[2]; // 保存要移除的值
    vec.erase(vec.begin() + 2);
    
    // 打印被移除的元素值
    std::cout << "移除了元素: " << removed_value << std::endl;
    
    // 打印移除后的向量
    std::cout << "移除后的向量: ";
    for (int num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    // 打印向量大小
    std::cout << "当前向量大小: " << vec.size() << std::endl;
    
    return 0;
}

三、关键特性与性能优化

  1. 与数组/链表对比

    • 优势:随机访问 O(1)、缓存友好(连续内存)、自动内存管理。
    • 劣势:中间插入/删除效率低(需移动元素)。
  2. 性能优化技巧

    • 预分配内存reserve() 减少扩容开销。
    • 使用 emplace_back:避免临时对象构造(尤其对复杂类型)。
    • 避免中间修改:频繁插入/删除时考虑 std::liststd::deque
  3. 高级用法

    • 二维 vectorvector<vector<int>> matrix(5, vector<int>(5, 0));
    • 排序与查找:结合 <algorithm> 中的 sort()find()

四、总结

  • 适用场景:需频繁随机访问、尾部操作居多的场景(如数据缓存、矩阵运算)。
  • 慎用场景:频繁在中间位置插入/删除数据(链表更优)。
  • 设计哲学:以空间换时间,通过动态扩容和连续内存实现高效访问,是现代 C++ 高性能容器的代表。

vector 之键值对

在 C++ 标准库中,std::vector 本身不直接支持键值对(Key-Value)结构,但可以通过组合其他类型(如 std::pair 或自定义结构)间接实现类似功能。以下是详细分析:

1. std::vector 的默认设计

  • 本质std::vector 是一个动态数组容器,存储单一类型的元素(如 intstring 等)。
  • 内存布局:元素在内存中连续存储,支持通过索引(vec[i])高效随机访问(时间复杂度 O(1))。
  • 无键值概念:原生不支持通过“键”(Key)查找或管理数据,仅依赖下标索引。

2. 如何实现键值对功能?

虽然 std::vector 不直接支持键值对,但可通过以下方式模拟:

方法一:使用 std::pair 存储键值对
#include <vector>
#include <utility> // for std::pair

std::vector<std::pair<std::string, int>> vec;
vec.push_back(std::make_pair("apple", 10)); // 添加键值对
vec.push_back(std::make_pair("banana", 20));

// 遍历查找(线性搜索,O(n))
for (const auto& kv : vec) {
    if (kv.first == "apple") {
        std::cout << "Found: " << kv.second << std::endl;
    }
}
  • 特点
    • 键值对作为整体存储在连续内存中。
    • 查找需遍历,效率较低(O(n)),适合少量数据或频繁遍历场景。
方法二:自定义结构体
struct KeyValue {
    std::string key;
    int value;
};
std::vector<KeyValue> vec;
vec.push_back({"apple", 10});
  • 适用场景:需扩展更多字段(如时间戳、状态等)。

3. 与专用键值容器的对比

以下对比 std::vector<std::pair>std::map/std::unordered_map

特性vector<std::pair>std::mapstd::unordered_map
内存连续性连续存储,缓存友好红黑树节点分散哈希表桶分散
查找复杂度O(n)(无序)/ O(log n)(有序+二分查找)O(log n)(自动排序)O(1) 平均(哈希表)
插入效率尾部插入 O(1),中间插入 O(n)O(log n)(需调整红黑树)O(1) 平均(可能触发 rehash)
内存占用⭐️ 低(仅需存储数据)⭐️⭐️ 高(每个节点含指针)⭐️⭐️ 高(桶+链表)
是否自动去重/排序需手动维护按键排序且去重去重,无序

4. 适用场景分析

  • 推荐用 vector<std::pair> 的情况

    • 数据量小(≤1000),需频繁遍历或按索引访问(如渲染数据列表)。
    • 键值对一次性加载,后续仅需遍历(如配置文件读取)。
    • 内存敏感场景(嵌入式系统)。
  • 推荐用 map/unordered_map 的情况

    • 需高频按键查找、插入或删除(如缓存、字典)。
    • 需自动去重或排序(如词频统计)。
    • 数据量较大(>1000)且对查找效率敏感。

5. 注意事项

  1. 查找效率vector 的线性查找效率低,若需高效查找,需先排序再用 std::binary_search(复杂度 O(log n))。
  2. 插入开销:在 vector 中间插入键值对需移动后续元素,避免频繁操作。
  3. 线程安全:与所有 STL 容器一样,多线程环境下需手动加锁。

6.总结

std::vector 本身不原生支持键值对,但可通过 std::pair 或自定义结构间接实现。其优势在于内存紧凑和遍历高效,劣势是查找效率低。若需高频按键操作,应优先选择 std::map(有序)或 std::unordered_map(无序哈希表)。
简单来说:用索引访问选 vector,用键名查找选 map

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值