序列式容器(Vector)

vector

std::vector 是 STL 提供的 内存连续的、可变长度 的数组(亦称列表)数据结构。能够提供线性复杂度的插入和删除,以及常数复杂度的随机访问。

为什么要使用 vector

  • vector 可以动态分配内存

很多时候我们不能提前开好那么大的空间(eg:预处理 1~n 中所有数的约数)。尽管我们能知道数据总量在空间允许的级别,但是单份数据还可能非常大,这种时候我们就需要vector来把内存占用量控制在合适的范围内。

vector 还支持动态扩容,在内存非常紧张的时候这个特性就能派上用场了。

  • vector 重写了比较运算符及赋值运算符

vector 重载了六个比较运算符,以字典序实现,这使得我们可以方便的判断两个容器是否相等(复杂度与容器大小成线性关系)。例如可以利用 vector 实现字符串比较(当然,还是用 std::string 会更快更方便)。另外 vector 也重载了赋值运算符,使得数组拷贝更加方便。

  • vector 便利的初始化

由于 vector 重载了 = 运算符,所以我们可以方便的初始化。此外从 C++11 起 vector 还支持 列表初始化,例如 vector data {1, 2, 3};。

构造函数

// 1. 创建空vector; 常数复杂度
vector<int> v0;

// 1+. 这句代码可以使得向vector中插入前3个元素时,保证常数时间复杂度
v0.reserve(3);

// 2. 创建一个初始空间为3的vector,其元素的默认值是0; 线性复杂度
vector<int> v1(3);

// 3. 创建一个初始空间为3的vector,其元素的默认值是2; 线性复杂度
vector<int> v2(3, 2);

// 4. 创建一个初始空间为3的vector,其元素的默认值是1,
// 并且使用v2的空间配置器; 线性复杂度
vector<int> v3(3, 1, v2.get_allocator());

// 5. 创建一个v2的拷贝vector v4, 其内容元素和v2一样; 线性复杂度
vector<int> v4(v2);

// 6. 创建一个v4的拷贝vector v5,其内容是{v4[1], v4[2]}; 线性复杂度
vector<int> v5(v4.begin() + 1, v4.begin() + 3);

// 7. 移动v2到新创建的vector v6,不发生拷贝; 常数复杂度; 需要 C++11
vector<int> v6(std::move(v2));  // 或者 v6 = std::move(v2);

元素访问

  1. at()
    v.at(pos) 返回容器中下标为 pos 的引用。如果数组越界抛出std::out_of_range 类型的异常。

  2. operator[]

    v[pos] 返回容器中下标为 pos 的引用。不执行越界检查。

  3. front()
    v.front() 返回首元素的引用。

  4. back()

    v.back() 返回末尾元素的引用。

  5. data()

    v.data() 返回指向数组第一个元素的指针。

长度和容量

vector 有以下几个与容器长度和容量相关的函数。注意,vector 的长度(size)指有效元素数量,而容量(capacity)指其实际分配的内存长度,相关细节请参见后文的实现细节介绍。

与长度相关:
  1. empty()
    返回一个 bool 值,即 v.begin() == v.end(),true 为空,false 为非空。

  2. size()
    返回容器长度(元素数量),即 std::distance(v.begin(), v.end())。

  3. resize()
    改变 vector 的长度,多退少补。补充元素可以由参数指定。

  4. max_size()
    返回容器的最大可能长度。

与容量相关:
  1. reserve()
    使得 vector 预留一定的内存空间,避免不必要的内存拷贝。

  2. capacity()
    返回容器的容量,即不发生拷贝的情况下容器的长度上限。

  3. shrink_to_fit()
    使得 vector 的容量与长度一致,多退但不会少。

元素增删及修改

  1. clear()
    清除所有元素

  2. insert()
    支持在某个迭代器位置插入元素、可以插入多个。复杂度与 pos 距离末尾长度成线性而非常数的。

  3. erase()
    删除某个迭代器或者区间的元素,返回最后被删除的迭代器。复杂度与 insert 一致。

  4. push_back()

在末尾插入一个元素,均摊复杂度为 常数,最坏为线性复杂度。

  1. pop_back()
    删除末尾元素,常数复杂度。

  2. swap()
    与另一个容器进行交换,此操作是 常数复杂度 而非线性的。

vector 的实现细节

vector 的底层其实仍然是定长数组,它能够实现动态扩容的原因是增加了避免数量溢出的操作。

首先需要指明的是 vector 中元素的数量(长度)n 与它已分配内存最多能包含元素的数量(容量) N 是不一致的,vector 会分开存储这两个量。

当向 vector 中添加元素时,如发现 n>N ,那么容器会分配一个尺寸为 2N 的数组,然后将旧数据从原本的位置拷贝到新的数组中,再将原来的内存释放。尽管这个操作的渐进复杂度是 O(n) ,但是可以证明其均摊复杂度为 O(1) 。

而在末尾删除元素和访问元素则都仍然是 O(1) 的开销。 因此,只要对 vector 的尺寸估计得当并善用 resize() 和 reserve(),就能使得 vector 的效率与定长数组不会有太大差距。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值