vector和array类似,都是数组,但是array是静态空间一旦配置了就不能改变,而vector是动态分配空间。他属于序列式容器 ( sequential containers )
所謂序列式容器,其中的元素都可序(ordered),但未排序(sorted)
下面主要讲解vector使用的几个易错区。
vector的迭代器虽然封装了指针,但是判断一个迭代器是不是空,不能直接利用NULL或nullptr,而是利用iterator!=vector.end()
具体参照下面代码,这是一个查找的操作,it指向了1的位置,而notFind没有找到合适的位置。
vector<int> test = {9,9,1,2,3};
vector<int>::iterator it = find(test.begin(), test.end(), 1);
vector<int>::iterator notFind = find(test.begin(), test.end(), 0);
if (it != test.end())
cout << *it << endl;
else
cout << "*it 不存在" << endl;
if (notFind != test.end())
cout << *notFind << endl;
else
cout << "*notFind 不存在" << endl;
输出:
vector的扩容根据编译器的不同而不同,有的扩容为原来的1.5倍,有的扩容为2倍等等,之所以采用成倍扩容的方式,是因为这样使得push_back操作拥有常数级时间复杂度。若每次扩容是以固定大小扩容的话,push_back操作的时间复杂度会变为O(n)
显然不是我们想要的。注意clear()之后,size为0,而capacity不变。下面展示了size和capacity之间的一些情况。
vector<int> test = {9,9,1,2,3};
cout << "size = " << test.size() << endl;
cout << "capacity = " << test.capacity() << endl;
/*插入一个新的元素*/
test.push_back(4);
cout << "size = " << test.size() << endl;
cout << "capacity = " << test.capacity() << endl;
test.clear();
cout << "size = " << test.size() << endl;
cout << "capacity = " << test.capacity() << endl;
输出:
注意vector的动态增加大小,并不是在原空间之后接连续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍(或1.5倍)另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原内容之后构建新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了,这是程序员易犯的一个错误。
例如看下面代码
vector<int> test = {9,9,1,2,3};
vector<int>::iterator it;
it = test.end()-1;
cout << *it << endl;
/*此时增加一个新元素会导致空间重新配置*/
test.push_back(4);
cout << *it << endl;
输出:
怎么解决这个问题呢?我们可以预测一下数据量,在声明完这个vector后,立刻利用reserve() 手动扩容。
下面简单讲下 pop_back, erase, clear, insert这四个操作。
// 將尾端元素拿掉,並調整大小。
void pop_back() {
--finish; // 將尾端標記往前移㆒格,表示將放棄尾端元素。
destroy(finish); // destroy 是全域函式,見第 2 章
}
erase(first,last)是清楚[first,last)前闭后开区间的元素,其中first和last都是迭代器,注意指向元素为空的迭代器在调用其指向的时候还是会报错的。因为erase并不会改变first和last 的指向。见下面二图即可
insert(iterator position, size_type n, const T& x)
从position位置开始,插入n个元素,他们的值为x。
时间复杂度总结:
随机访问,时间复杂度为 O(1)
在未尾插入或删除元素,整体分摊的时间复杂度为 O(1)
其它位置插入或删除元素,与当前位置至向量末尾的距离有关,时间复杂度 O(n)