书接上期,这一期我们开始讲解STL容器里面的“维克托”也是最常用的容器之一。

向量(Vector)是一个封装了动态大小数组的顺序容器。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)。
本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
在学习STL容器时有众多相对应的接口,和许多算法,因此一定要学会看帮助手册以及文档。下面我将列出vector所有常用的接口,并与之对应给出示例帮助学习和记忆。
- 使用vector容器时需要添加头文件
- #include
vector迭代器常用的成员函数介绍:
begin()函数:
函数原型: iterator begin(); const_iterator begin(); 功能:返回一个当前vector容器中起始元素的迭代器。end()函数:
函数原型: iterator end(); const_iterator end(); 功能:返回一个当前vector容器中末尾元素的迭代器。 注意end()指向的是最后一个元素的下一个位置,所以访问最后一个元素rbegin()函数:
返回指向最后一个元素的反向迭代器rend()函数:
返回指向第一个元素之前一个位置的反向迭代器。
vector的构造函数操作:
vectorv;//采用模板实现类实现,默认构造函数 vector(v.begin(), v.end());//将v.begin()到 v.end()区间的元素拷贝给本身 vector(n, elem);//构造函数,将n个elem元素拷贝给自身 vector(const vector & vec);//拷贝构造函数 eg使用第二个构造函数 vector<int>v(arr, arr + sizeof(arr) / sizeof(int));
示例:
下面我会编写一个打印函数,以供后面所有示例去调用,不会再重复编写。
- void Show(vector<int>& v1)
- {
- vector<int>::iterator it;
- for (it = v1.begin(); it != v1.end(); ++it)
- {
- cout << *it << " ";
- }
- cout << endl;
- }
- void test02()
- {
- vector<int> v1(5, 100);//用5个100来构造自身,调用第三个拷贝构造
- Show(v1);
-
- vector<int>v2 = v1;//旧对象给新对象赋值,调用第四个拷贝构造
- Show(v2);
-
- vector<int>v3 (v1.begin(), v1.end());//区间构造,调用第二个拷贝构造
- Show(v3);
- }
- int main()
- {
- test02();
- return 0;
- }

vector的常用赋值操作:
assign(begin, end);//将(begin,end)区间的数据拷贝赋值给自身 assign(n, elem);//将n个elem拷贝赋值给自身 vector& operator=(const vector & vec);//重载等号赋值运算符 swap(vec);//将vec与本身元素互换
示例:
- void test02()
- {
- vector<int> v5 = { 1,2,3,4,5 }; //列表初始化,注意使用的是花括号
-
- vector<int> v1(5, 100);//用5个100来构造自身,调用第三个拷贝构造
- Show(v1);
-
- vector<int>v2 = v1;//旧对象给新对象赋值,调用第四个拷贝构造
- Show(v2);
-
- vector<int>v3 (v1.begin(), v1.end());//区间构造,调用第二个拷贝构造
- Show(v3);
-
- vector<int> v4;
- v4 = v3;//使用重载的赋值运算符
- Show(v4);
- v4.assign(5, 10);//给v4赋5个10
- Show(v4);
- v4.assign(v1.begin(), v1.end());//区间赋值
- Show(v4);
- }
- int main()
- {
- test02();
- return 0;
- }

vector的大小容量操作:
vector大小容量操作 size();//返回容器中元素个数 empty();//判断容器是否为空 capacity();//容器的容量 resize(int num);//更改大小,重新指定容器的长度为num,若容器变长,则以默认值填充新位置 如果容器变短,则末尾超出容器长度的元素被删除 resize(int num, elem);//更改大小,重新指定容器的长度为num,若容器变长,则以elem填充新位置 如果容器变短,则末尾超出容器长度的元素被删除 reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问
示例:
- void test02()
- {
- vector<int> v1(5, 100);//用5个100来构造自身,调用第三个拷贝构造
-
- vector<int>v3 (v1.begin(), v1.end());//区间构造,调用第二个拷贝构造
-
-
- vector<int> v4;
- v4 = v3;//使用重载的赋值运算符
- v3.assign(10,1);//给v3赋10个1
- Show(v3);
- v3.swap(v4);//交换v3,v4的值
-
- //大小,容量
- cout << "大小:" << v4.size() << "容量:" << v4.capacity() << endl;
- //容器是否为空
- vector<int>v5;
- if (v5.empty())
- {
- cout << "为空" << endl;
- }
- else {
- cout << "非空" << endl;
- }
-
- vector<int>v6(10, 30);
- v6.resize(20);//重置长度为20,过大后补0
- cout << "大小:" << v6.size() << "容量:" << v6.capacity() << endl;
- Show(v6);
- v6.resize(25, 5);//重置长度为25,过大后补5
- cout << "大小:" << v6.size() << "容量:" << v6.capacity() << endl;
- Show(v6);
-
- vector<int>v7;
- cout << "大小:" << v7.size() << "容量:" << v7.capacity() << endl;
- v7.reserve(100);//预留了100的容量空间
- cout << "大小:" << v7.size() << "容量:" << v7.capacity() << endl;
- }
- int main()
- {
- test02();
- return 0;
- }

vector的插入和删除操作:
insert(const iterator pos, int count, ele);//迭代器指向pos位置插入count个元素ele push_back(elem);//尾部插入元素elem pop_back();//删除最后一个元素 erase(const iterator start, const iterator end);//删除迭代器start到end间的元素 erase(const iterator pos);//删除迭代器指向的元素 clear();//删除容器中所有元素
示例:
- void test02()
- {
- vector<int>v8;
- v8.push_back(1); //插入元素
- v8.push_back(2);
- v8.push_back(3);
- v8.pop_back();//尾删
- Show(v8);//1 2
-
- vector<int>v9;
- v9.insert(v9.begin(), 5, 2);//使用迭代器插入
- Show(v9);//2 2 2 2 2
- v9.insert(v9.begin()+2,2, 19);
- Show(v9);//2 2 19 19 2 2 2
- v9.insert(v9.end(), 2, 15);
- Show(v9);//2 2 19 19 2 2 2 15 15
-
- v9.erase(v9.begin(), v9.begin()+3);//删除了前三个元素
- Show(v9);
-
- v9.clear();//清空内容大小,容量不变
- cout << "大小:" << v9.size() << "容量:" << v9.capacity() << endl;
-
- }
- int main()
- {
- test02();
- return 0;
- }

vector的数据存取操作:
at(int index);//返回索引index所指的数据,如果index越界,跑出out of range异常 operator[];//返回索引idx所指的数据,越界时,直接报错 front();//返回容器中第一个数据元素 back();//返回容器中最后一个元素
- void test02()
- {
- vector<int>v8;
- v8.push_back(1); //插入元素
- v8.push_back(2);
- v8.push_back(3);
- cout << "头元素:" << v8.front() << " 尾元素:" << v8.back() << endl;
- //at越界会跑出异常,【】不会抛出异常
- cout << v8.at(1) << " " << v8[1] << endl;//取元素,下标从0开始
- v8.at(0) = 10;//赋值,更改
- v8[2] = 15;
- Show(v8);//10 2 15
-
- v8.pop_back();//尾删
- Show(v8);//10 2
- }
- int main()
- {
- test02();
- return 0;
- }

vector常用的创建,循环插入,遍历:
int main() { vector<int> x{1,2,3,4,5};//初始化x,赋值为1 2 3 4 5 //vectorx={1,2,3,4,5}等价 //遍历 for (auto num : x) { cout << num << " "; } cout << endl; x.clear();//清空 // 插入 for (int i = 0; i < 5; i++)//循环插入1 2 3 4 5 { x.push_back(i+1); } //遍历 for (vector<int>::iterator it = x.begin(); it != x.end(); it++) { cout << (*it) << " "; } cout << endl; //遍历 for (auto it = x.begin(); it != x.end(); it++) { cout << (*it) << " "; } cout << endl; //遍历 for (int j = 0; j < 5; j++) { cout << x[j] << " "; } cout << endl; vector<int>p(5); //容器开始就有5个元素,它们的默认初始值都为 0。 for (int j = 0; j < p.size(); j++) { cout << p[j] << " "; } cout << endl; cout << p.front() << endl; cout << p.size() << endl; }
在C++标准库容器vector的容量是不会自动的缩减的,也就是说删除元素操作,
其引用、指针、迭代器也会继续有效。
那么当在一个较大的vector中删除了大量的元素之后,
其实际的size比较小,而其capacity比较大,如果对空间比较敏感,
希望vector的容量能够缩小一些,这时可以使用下面的技巧来实现。
- void test03()
- {
- vector<int>v1;
- v1.reserve(500);
- v1.assign(5, 60);
- cout << "大小:" << v1.size() << "容量:" << v1.capacity() << endl;
- //使用拷贝构造函数创建匿名对象,调用匿名对象的swap函数
- //匿名对象在拷贝函数代码行结束后即被系统回收,实现了空间缩小
- //匿名对象在拷贝时只拷贝了他的有效空间
- vector<int>(v1).swap(v1);
-
- cout << "大小:" << v1.size() << "容量:" << v1.capacity() << endl;
- }
- int main()
- {
- test03();
- return 0;
- }
我们会发现容量仅仅减少为之前的有效容量

二维数组其实就是一个数组包着许多一维数组构成,那么vector创建一维动态数组,如果创建二维也只需要"vector包着vector即可"
- void test04()
- {
- vector<int>v1(5, 10);
- vector<int>v2(4, 20);
- vector<int>v3(3, 30);
- //定义一个容器 存放v1 v2 v3
- //容器嵌套容器,二维数组
- vector
int>>v; - v.push_back(v1);//插入
- v.push_back(v2);
- v.push_back(v3);
- //遍历 利用数组方式遍历
- for (int i = 0; i < v.size(); ++i)
- {
- for (int j = 0; j < v[i].size(); ++j)
- {
- cout << v[i][j] << " ";
- }
- cout << endl;
- }
- //利用迭代器的方式遍历
- vector
int>>::iterator it; - for (it = v.begin(); it!= v.end(); ++it)
- {
- vector<int>::iterator mit;
- for (mit = (*it).begin(); mit != (*it).end(); ++mit)
- {
- cout << *mit << " ";
- }
- cout << endl;
- }
- }
- int main()
- {
- test04();
- return 0;
- }

STL提供了大量的算法便于操作,这里我们仅用sort最简单的排序函数演示如何操作,后续将会吧所有常用的算法使用出一期文章。
要了解:
算法主要是由头文件
- void test05()
- {
- vector<int>v;
- v.push_back(5);//插入
- v.push_back(2);
- v.push_back(3);
- v.push_back(6);
- Show(v);
- //sort算法排序
- //包含头文件#include
算法头文件 - sort(v.begin(), v.end());//默认从小到大排
- Show(v);
- sort(v.begin(), v.end(),greater<int>());//从大到小排 加个规则greater
() - Show(v);
- }
- int main()
- {
- test05();
- return 0;
- }

在C++中对于类的使用是常态,因此对于vector操作自定义的数据类型要掌握。
- class Person
- {
- private:
- int num;
- string name;
- public:
- Person(){}
- Person(int num1, string name1) :num(num1), name(name1)
- {
- cout << "有参构造" << endl;
- }
- void Setnum(int num1) { this->num = num1; }
- void Setname(string name1) { this->name = name1; }
- int Getnum() {
- return num;
- }
- string Getname()
- {
- return name;
- }
-
- };
- void test06()
- {
- vector
v; - v.push_back(Person(12, "lucy"));
- v.push_back(Person(20, "Joe"));
- v.push_back(Person(36, "Tom"));
- vector
::iterator it; - for (it = v.begin(); it != v.end(); ++it)
- {
- cout << "年龄: " << (*it).Getnum() << "姓名:" << (*it).Getname();
- cout << endl;
- }
- 注意:对于自定义容器的排序,必须自己实现排序规则
- 或重载自定义数据类型的< 或>
- }
- int main()
- {
- test06();
- return 0;
- }

看到这里相信对于vector的操作与使用就有了一定的了解,后续容器的很多使用也与vector近似,这一系列也会持续更新下去,感谢观看!