文章目录
描述
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
一、vector(标准库)
常见接口
vector使用
遍历
查找元素,插入元素
构造函数
我们在以前写杨辉三角的时候使用的是一个二维数组,在学习完vector后,我们也可以使用vector解决杨辉三角的问题。
class Solution {
public:
vector<vector<int>> generate(int numRows) {
//这里的vector<vector<int>>我们就可把他看做一个二维数组,由于内部实现一些函数,
//所以可以使用[]直接像数组那样使用,但是不是数组,是内部实现的运算符重载
vector<vector<int>> vv;
vv.resize(numRows,vector<int>());
for(size_t i=0;i<vv.size();i++)
{
vv[i].resize(i+1,0); //扩容
vv[i][0]=vv[i][vv[i].size()-1]=1; //将最外层初始化为1
}
for(size_t i=0;i<vv.size();i++)
{
for(size_t j=0;j<vv[i].size();j++)
{
if(vv[i][j]==0)
{
vv[i][j]=vv[i-1][j]+vv[i-1][j-1];
}
}
}
return vv;
}
};
二、vector模拟实现
1.默认成员函数
template<class T>
class vector
{
public:
//迭代器
typedef T* iterator;
typedef const T* const_iterator;
//空参的构造函数
vector()
{}
vector(size_t n, const T& val = T())
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
//重载,为防止构造器匹配到别的函数模板,需重载一个int类型的
vector(int n, const T& val = T())
{
reserve(n);
for (int i = 0; i < n; ++i)
{
push_back(val);
}
}
//支持任意类型的迭代器构造,我们传迭代器可以传入不同类型,需要写泛型函数模板
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
first++;
}
}
//拷贝构造
vector(const vector<T>& v)
{
//提前开好所用空间,可以提高效率
reserve(v.capacity());
_start = new T[v.capacity()];
//注意:memcpy(_start, v._start,sizeof(T) * v.size()); //不能完成深拷贝
//使用拷贝构造就是深拷贝
for (size_t i = 0; i < v.size(); ++i)
{
_start[i] = v._start[i];
}
//更新
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
//析构函数
~vector()
{
//通过new出来的空间需要使用delete手动使用,再将指针指向空,防止内存泄露
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
private:
//C++11 内置类型可以传缺省值,这里的缺省值传给初始化列表初始化
iterator _start=nullptr; //指向数组开头的指针
iterator _finish=nullptr; //指向数组最后一个有效元素的指针
iterator _end_of_storage=nullptr; //指向数组最后一个位置的指针
};
2.resize
//开空间 + 初始化
void resize(size_t n,const T val=T()) //使用匿名函数初始化,这里为了可以初始化,可以使用 int a =int();
{
if (n < size())
{
//n<size,说明要调整的大小比我们原来的要小,删除数据,其实就是将指针位置改变一下,不需要真的删除元素
_finish = _start + n;
}
else
{
if (n > capacity())
{
//这里 n>capacity 这种情况说明,要调整的大小比我们最大容量还要大,此时我们需要扩容
reserve(n);
}
while (_finish != _start + n)
{
*_finish = val;
++_finish;
}
}
}
3.reserve
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
//申请空间
T* tmp = new T[n];
//如果原数组中有元素,拷贝到新申请的空间上
if (_start)
{
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
//更新
_start = tmp;
_finish = _start + sz; //小坑,不可使用size(),因为更新完_start后size()发生改变
_end_of_storage = _start + n;
}
}
4.尾插,尾删
//尾插
void push_back(const T& x)
{
if (_finish == _end_of_storage)
{
//扩容
reserve(capacity()==0?4:capacity()* 2);
}
*_finish = x;
++_finish;
}
//尾删
void pop_back()
{
assert(!empty()); //断言vector是否为空
--_finish;
}
5.insert(插入)
void insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _end_of_storage)
{
//迭代器失效问题,需要更新pos,因为我们使用reserve是异地扩容,迭代器的值已被改变,我们需要记录一下相对位置
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
//扩容后更新pos,解决pos失效问题
pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = val;
++_finish;
}
6.erase(删除)
iterator erase(iterator pos)
{
//也有迭代器失效问题,野指针 所以我们在删除指定位置的元素后,不可再使用原来的迭代器进行操作,返回一个新的迭代器可解决此问题
assert(pos >= _start);
assert(pos < _finish);
iterator start = pos + 1;
//挪动数据
while (start != _finish)
{
*(start - 1) = *start;
++start;
}
--_finish;
return pos;
}
7. [] 运算符重载
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
8.empty(判空),capacity(容量大小),size(元素个数)
bool empty()
{
return _start == _finish;
}
size_t capacity() const
{
return _end_of_storage - _start; //指针 - 指针
}
size_t size() const
{
return _finish - _start;
}