目录
本篇先对vector做一个介绍,然后把常用的接口演示一下。
由于vector的许多接口和string那儿的高度重合,所以这里就不详讲了,主要做一个了解。只要掌握几个最重要的接口,其余的用到时 查文档就好。
vector是一个类模板。它能够容纳各种类型的对象作为其元素,并且可以动态地调整大小。可以理解为动态数组。
它像数组,又不像。
像数组的是:vector也采用的连续存储空间来存储元素。这意味着可以采用下标对vector的元素进行访问,和数组一样高效。
不像数组的是:它的大小是可以动态改变的,而且它的大小会被容器自动处理。
本质来说,vector使用动态分配数组来存储它的元素。
当新元素插入时候,这个数组需要被重新分配大小,为了增加存储空间。
其做法是,分配一个新的数组,然后将全部元素移到这个数组。
就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。
不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
与其它动态序列容器相比(deque、list 、forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。
对于其他不在末尾的删除和插入操作,效率更低。
包头文件:
#include
两个标了🚩的要记住:
构造函数声明 | 接口说明 |
---|---|
vector();(重点)🚩 | 无参构造 |
vector(size_type n, const value_type& val = value_type()); | 构造并初始化n个val |
vector (const vector& x); 🚩 | 拷贝构造 |
vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
我们在使用时,要注意vector是一个类模板,要实例化成 具体的类型 来使用:
- vector<int> v1; //创建一个int类型的,空的v1
-
- vector<int> v2(5, 1); //v2含5个1
-
- vector<int> v3(v2); //拷贝构造
-
- vector<int> v4(v3.begin(), v3.end()); //用迭代器
vector没有头插 / 头删(因为每次都要挪数据,效率太低),也不能用operator+=,
它只能尾插 / 尾删,或者insert/erase。总之,就是一个一个地插入、删除。
- #include
- using namespace std;
-
- int main()
- {
- vector<int> v1;
- v1.push_back(1);
- v1.push_back(2);
- v1.push_back(3);
- v1.push_back(4);
- return 0;
- }
相关接口:尾删pop_back()
先说明一下:
vector是不能直接用流插入输出的:
因为vector是一个容器,它可以存储多个元素。
而流插入输出操作符<<是用于将一个单独的元素插入到流中的,而不是将整个容器插入到流中。
因此,如果要将vector中的元素输出到流中,需要使用循环逐个输出。
现在我们就来看看遍历输出vector的4种方法:
vector是支持operator[]和size()的,因此可以用 方括号+下标 的方式遍历。
- #include
- #include
- using namespace std;
-
- int main()
- {
- vector<int> v;
- v.push_back(1);
- v.push_back(2);
- v.push_back(3);
- v.push_back(4);
-
- for (size_t i = 0; i < v.size(); i++)
- {
- cout << v[i] << " ";
- }
- return 0;
- }
at()是像函数一样调用的:
- for (size_t i = 0; i < v.size(); i++)
- {
- cout << v.at(i) << " ";
- }
这里补充一下at()和operator[]的区别:两者检查越界的方式不一样。
at()是较温和的抛异常:
而operator[]是断言:
- vector<int>::iterator it = v.begin();
- while (it != v.end())
- {
- cout << *it << " ";
- it++;
- }
- for (auto& e : v)
- {
- cout << e << " ";
- }
容量空间 | 接口说明 |
---|---|
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
resize (重点) | 改变vector的size |
reserve (重点) | 改变vector的capacity |
这些接口的功能、用法都和string里的类似,就不详讲了。
这里要说下,capacity在vs下和在g++下的增长问题:
vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
来测试下:
vs
- #include
- #include
- using namespace std;
-
- int main()
- {
- vector<int> v;
- int val = v.capacity();
- for (size_t i = 0; i < 100; i++)
- {
- v.push_back(i);
- if (val != v.capacity())
- {
- cout <<"now the capacity is: "<< v.capacity() << endl;
- }
- val = v.capacity();
- }
- return 0;
- }
g++:
assign:将新内容指定给vector,替换其当前内容,并相应地修改其size。也就是说,起到一个替换、覆盖的作用。
演示:
- #include
- #include
- using namespace std;
-
- int main()
- {
- vector<int> v;
- v.push_back(1);
- v.push_back(2);
- v.push_back(3);
- for (auto& e : v)
- {
- cout << e << " ";
- }
- cout << endl;
-
- v.assign(8, 8);
- for (auto& e : v)
- {
- cout << e << " ";
- }
- return 0;
- }
其实vector的接口里并未提供find(),不光是vector,list、deque等容器中也没有该函数。
这是因为像vector、list、deque等容器,它们都是从头到尾遍历查找,查找逻辑是相同的。
而在算法库
注意:
1.要包
2.迭代器区间,都是左闭右开的 :[ first,last )
3.返回的是迭代器。如果找到了val,返回val的迭代器;如果没找到,返回last,即右边开区间的位置。
示例:
- #include
- #include
- #include
- using namespace std;
-
- int main()
- {
- vector<int> v;
- v.push_back(1);
- v.push_back(2);
- v.push_back(3);
- v.push_back(4);
- vector<int>::iterator it = find(v.begin(), v.end(), 3);
- cout << *it << endl;
- return 0;
- }
有了insert(),我们可以实现自由插入了,包括头插。
现在我们就在2的前面插入一个100:
- int main()
- {
- vector<int> v;
- v.push_back(1);
- v.push_back(2);
- v.push_back(3);
-
- vector<int>::iterator pos = find(v.begin(), v.end(), 2); //先找到2的位置pos
- if (pos != v.end())
- {
- vector<int>::iterator it = v.insert(pos, 100); //在pos位置插入100,2被挤到后一个
- }
-
- for (auto& e : v)
- {
- cout << e << " ";
- }
- return 0;
- }
示例:
- int main()
- {
- vector<int> v;
- v.push_back(1);
- v.push_back(2);
- v.push_back(3);
- v.push_back(4);
-
- vector<int>::iterator pos = find(v.begin(), v.end(), 2);
- if (pos != v.end())
- {
- v.erase(pos);
- }
- for (auto& e : v)
- {
- cout << e << " ";
- }
- return 0;
- }
sort()和find()一样,都是在
➡️排序算法默认排的是升序,例:
- #include
- #include
- #include
- using namespace std;
- int main()
- {
- vector<int> v;
- v.push_back(11);
- v.push_back(8);
- v.push_back(3);
- v.push_back(4);
- v.push_back(0);
- v.push_back(4);
- v.push_back(9);
- v.push_back(7);
-
- for (auto e : v)
- {
- cout << e << " ";
- }
- cout << endl;
-
- sort(v.begin(), v.end());
- for (auto e : v)
- {
- cout << e << " ";
- }
- cout << endl;
- return 0;
- }
➡️那要怎么排降序呢?
用仿函数(仿函数目前不详讲,现在会用就行),库里面已经写好了,直接拿来用即可。
- template <class RandomAccessIterator, class Compare>
- void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp); //仿函数Compare comp
两个仿函数,一个叫less(<,升序),一个叫greater(>,降序),两个都是类模板。
注意:在使用时要包
示例:
- #include
- #include
- #include
- #include
- using namespace std;
-
- int main()
- {
- vector<int> v;
- v.push_back(11);
- v.push_back(8);
- v.push_back(3);
- v.push_back(4);
- v.push_back(0);
- v.push_back(4);
- v.push_back(9);
- v.push_back(7);
-
- for (auto e : v)
- {
- cout << e << " ";
- }
- cout << endl;
-
- less<int> lt;
- greater<int> gt;
- sort(v.begin(), v.end(), lt); //升序
- for (auto e : v)
- {
- cout << e << " ";
- }
- cout << endl;
-
- sort(v.begin(), v.end(), gt); //降序
- for (auto e : v)
- {
- cout << e << " ";
- }
- cout << endl;
-
- return 0;
- }
不过,更常见的是用匿名对象来写:
sort(v.begin(), v.end(), greater<int>());
既然vector是类模板,那vector
答案当然是不能。
原因:
1.string结尾是有'\0'的,而vector没有。
2.很多string的接口,vector
所以,几乎不会用vector