1、vector的介绍
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自 动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。
- 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和 forward_lists统一的迭代器和引用更好。
注意:string与vector类似,但也有不同之处。string是存放字符的动态数组,vector是存放一个T类型的动态数组,本质都是一个顺序表。并且我们使用vector时需要加头文件#include<vector>
2、vector的常用接口
构造函数
void test_vector1()
{
vector<int> v1;
vector<int> v2(10, 8);//开十个空间都赋值8
vector<int> v3(++v2.begin(), --v2.end());//用自身的迭代器区间构造
vector<int> v4(v3);//拷贝构造
string s("hello world");
vector<char> v5(s.begin(), s.end());//用string的迭代器区间构造
}
尾插和尾删
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.pop_back();
v.pop_back();
v.pop_back();
v.pop_back();
由于头删和头插效率太低了,vector并没有提供相应的接口。
遍历和[]访问
vector可支持[]下标访问,并且可以改变vector中的值,至于遍历,我们可以用下标遍历,也可以使用范围for来遍历。
//遍历
for (size_t i = 0; i < v.size(); i++)
{
v[i] += 1;//可修改
cout << v[i] << " ";
}
cout << endl;
//
vector<int>::iterator it = v.begin();
//迭代器 it指向vector的第一个数据
while (it != v.end())
{
*it -= 1;
cout << *it << " ";
it++;
}
cout << endl;
//范围for 本质也是迭代器
for (auto e : v)
//把v中的每个数据遍历的时候给给e
{
cout << e << " ";
}
cout << endl;
//int a[] = { 1,2,3 };
原生指针就是天然的迭代器,数组支持范围for,会被替换指针
//for (int* p = a; p < a + sizeof(a) / sizeof(int); p++)
//{}
reserve和resize的使用
v.reserve(100); //扩容并只改变capacity空间
v.resize(100); //扩容不仅改变capacity空间并且改变size
v.resize(100, 5); //扩容+初始化 或 删除数据(类比string)
v.resize(2); //截取数据但不改变capacity
find和assign的使用
vector<int>::iterator ret = find(v.begin(), v.end(), 3);
//auto ret = find(v.begin(), v.end(), 3);
//如果没找到find返回第二个参数
//如果找到了返回该数下标
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.assign(10, 5);//给v的前十个值上全部赋值为5,并且会覆盖原来的值
clear、capacity、size、max_size的使用
cout <<"max_size:"<< v.max_size() << endl;//数据所能存放的最大容量
cout <<"capacity:"<< v.capacity() << endl;//当前容量
cout <<"size:"<< v.size() << endl;//当前数据个数
v.clear();//只清理size的值不改变capacity
cout << "-------------------";
cout << "max_size:" << v.max_size() << endl;
cout << "capacity:" << v.capacity() << endl;
cout << "size:" << v.size() << endl;
insert、erase的使用
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//v.assign(10, 5);
//覆盖之前已有的值
vector<int>::iterator ret = find(v.begin(), v.end(), 3);
//auto ret = find(v.begin(), v.end(), 3);
//如果没找到find返回第二个参数
if (ret != v.end())
{
cout << "找到了" << endl;
v.insert(ret, 30);
//v.erase(ret);
//不能在这删,因为ret失效了
//这个迭代器失效问题,我们后面会细讲
}
v.insert(v.begin(), 30);//在v.begin()的前面插入30也就是头插
vector<int>::iterator pos = find(v.begin(), v.end(), 300);
if (pos != v.end())
//找不到的时候就会返回v.end
//因此找不到的时候不会进入
{
v.erase(pos);//删除pos下标的值
}
增容方式
int main()
{
size_t sz;
std::vector<int> foo;
sz = foo.capacity();
std::cout << "making foo trow;\n";
for (int i = 0; i < 100; i++)
{
foo.push_back(i);
if (sz != foo.capacity())
{
sz = foo.capacity();
std::cout << "capacity changed:" << sz << '\n';
}
}
}
由上图可知vs下vector是以大约1.5增容的。而Linux下g++是以2倍增容的。
vector相关OJ题
1、只出现一次的数
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int value=0;
for(auto e : nums)
{
value^=e;//将nums中的值全部互相异或,这样的话相同的数据刚好抵消
}
return value;//返回剩下的那个与其他值不相等的数据
}
};
2、杨辉三角形
杨辉三角形
class Solution {
public:
vector<vector<int>> generate(int numRows)
{
vector<vector<int>> vv;
vv.resize(numRows);//先给外层vector开内存
for (size_t i = 0; i < numRows; i++)
{
vv[i].resize(i + 1);//给每一个小vector开内存
vv[i][0] = vv[i][vv[i].size() - 1] = 1;//小vector中的头尾设为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;
}
};
3、电话号码的字母组合(重点题,代码中有详细注释)
//回溯力扣题:《电话号码的字母组合》
class Solution {
string arr[10] = { "","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz" };
//将0-9(其中0和1不给值)中的每个数字映射一个字符串
public:
//最关键的一个函数体,会反复使用
void _letterCombinations(const string& digits, size_t i,
string combinStr, vector<string>& strV)
//传入digits的引用并且加const为了防止改变digits并且减少拷贝,digits就是我们传入的字符串
//传入i为了遍历digits中的每个字符串,因为digits字符串的每个字符还代表这一个字符串:例如2->"abc"
//combinStr是为了去存放每次递归后的字符串组合
//strV为了存放所有情况遍历完全之后的字符串总和
{
if (i == digits.size())
//由于i是从0开始的,倘若此时i已经与digits的长度相等那么说明i已经越界了也代表digits的字符串已经遍历完全了
{
strV.push_back(combinStr);
//将我们组合好的combinStr尾插到strV中,strV也就是最终我们需要返回的那个字符串
return;//立即返回,只要执行该语句,该函数将不再执行接下来的语句,因为此时就需要返回
}
string str = arr[digits[i] - '0'];
//定义一个新的字符串str并且让str中存入我们需要遍历的字符串
//digits[i]是访问到我们输入字符串中的某个字符
//digits-'0'为了得到arr的下标 使其得到digits字符串中每个字符中的字符串
//最后让它赋值给str我们就可以遍历str将其进行组合
for (size_t j = 0; j < str.size(); j++)//遍历str进行组合,并且继续递归调用_letterCombination函数
{
_letterCombinations(digits, i + 1, combinStr + str[j], strV);
//传入digits 为了继续访问它的字符中的字符串
//传入i+1 为了访问digits中的下一个字符中的字符串 并且这样的话i不会发生改变 以便下次可以直接继续使用i+1
//传入 combinStr+str[j] 为了将我们得到的str中的字符尾插到combinStr中存放起来,等遍历结束可以尾插到strV中
//传入 strV 如果遍历结束可直接把combinStr中的值直接传入strV中
}
}
vector<string> letterCombinations(string digits)
//digits是我们所选择的字符串
{
string combinStr;//定义一个combinStr字符串
vector<string> strV;//定义一个vector容器里面的值是string值
if (digits.empty())//为了考虑我们的digits为空的情况下
return strV;//若为空 直接返回
//开始递归调用此函数
_letterCombinations(digits, 0, combinStr, strV);
return strV;//递归调用完成后返回
}
};
图解:
3、vector的模拟实现
vector的成员变量
private:
iterator _start; // 指向数据块的开始
iterator _finish; // 指向有效数据的尾
iterator _endofStorage; // 指向存储容量的尾
构造函数
默认构造
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
迭代器构造
//一个类模板的成员函数,又可以是一个函数模板
template<class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
while (first != last)
{
push_back(*first);
first++;
}
}
拷贝构造
//v2(v1)
//现代写法
vector(const vector<T>& v)
{
vector<T> tmp(v.begin(),v.end());
//没有模拟swap之前可用下列写法
/*swap(_start, tmp._start);
swap(_finish, tmp._finish);
swap(_endofstorage, tmp._endofstorage);*/
//this->swap(tmp);
swap(tmp);
}
v2(v1)传统写法
//vector(const vector<T>& v)
// //拷贝构造(深拷贝)
//{
// _start = new T[v.capacity()];
// _finish = _start + v.size();
// _endofstorage = _start + v.capacity();
// memcpy是浅拷贝
// memcpy(_start, v._start, v.size() * sizeof(T));
//}
这里传统写法不是很推荐,因为会用到memcpy,因为memcpy会用到浅拷贝。
析构函数
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
赋值运算符重载函数
//v1=v2
//现代写法
vector<T>& operator=(vector<T> v)
{
//未模拟swap时用下列方法
/*swap(_start, v._start);
swap(_finish, v._finish);
swap(_endofstorage, v._endofstorage);*/
swap(v);
return *this;
}
迭代器
public:
typedef T* iterator;
typedef const T* const_iterator;
begin()和end()
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
容量函数
size()和capacity()
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
reserve
void reserve(size_t n)
{
if (n > capacity())
{
//扩容
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy是浅拷贝
//memcpy(tmp, _start, sizeof(T) * size());
for (size_t i = 0; i < sz; i++)
{
//T 是int,一个一个拷贝没问题
//T 是string,一个一个拷贝调用是T的深拷贝复制,也没问题
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
/*_finish = tmp + size();
_start = tmp;*/
_endofstorage = _start + n;
}
}
resize
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
while (_finish != _start + n)
{
*_finish = val;
_finish++;
}
}
}
empty
bool empty()const
{
return _start == _finish;
}
增删查改
push_back
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
扩容
//size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
size_t sz = size();
//T* tmp = new T[newCapacity];
//if (_start)
//{
// memcpy(tmp, _start, sizeof(T) * size());
// delete[] _start;
//}
_start = tmp;
_finish = _start + sz;
//_finish = tmp + size();
//_start = tmp;
//_endofstorage = _start + newCapacity;
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
_finish++;
}
pop_back
void pop_back()
{
assert(_finish > _start);//检查是否为空
_finish--;
}
insert
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
//满了就扩容
if (_finish == _endofstorage)
{
//扩容
//扩容会导致pos失效,扩容需要更新一下pos
size_t len = pos - _start;//计算一下扩容前pos与_start直接的距离以便以计算新pos
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;//得到新pos的位置
}
//移动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = x;
_finish++;
return pos;
}
earse
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos <= _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
begin++;
}
_finish--;
return pos;
}
swap
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
下标访问
T& operator[](size_t i)
{
assert(i < size());
return _start[i];
}
完整代码
#pragma once
namespace mwb
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
// v2(v1)传统写法
//vector(const vector<T>& v)
// //拷贝构造(深拷贝)
//{
// _start = new T[v.capacity()];
// _finish = _start + v.size();
// _endofstorage = _start + v.capacity();
// memcpy是浅拷贝
// memcpy(_start, v._start, v.size() * sizeof(T));
//}
//一个类模板的成员函数,又可以是一个函数模板
template<class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
while (first != last)
{
push_back(*first);
first++;
}
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
//v2(v1)
//现代写法
vector(const vector<T>& v)
{
vector<T> tmp(v.begin(),v.end());
/*swap(_start, tmp._start);
swap(_finish, tmp._finish);
swap(_endofstorage, tmp._endofstorage);*/
//this->swap(tmp);
swap(tmp);
}
//v1=v2
//现代写法
vector<T>& operator=(vector<T> v)
{
/*swap(_start, v._start);
swap(_finish, v._finish);
swap(_endofstorage, v._endofstorage);*/
swap(v);
return *this;
}
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
T& operator[](size_t i)
{
assert(i < size());
return _start[i];
}
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
void reserve(size_t n)
{
if (n > capacity())
{
//扩容
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy是浅拷贝
//memcpy(tmp, _start, sizeof(T) * size());
for (size_t i = 0; i < sz; i++)
{
//T 是int,一个一个拷贝没问题
//T 是string,一个一个拷贝调用是T的深拷贝复制,也没问题
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
/*_finish = tmp + size();
_start = tmp;*/
_endofstorage = _start + n;
}
}
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
while (_finish != _start + n)
{
*_finish = val;
_finish++;
}
}
}
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
//满了就扩容
if (_finish == _endofstorage)
{
//扩容
//扩容会导致pos失效,扩容需要更新一下pos
size_t len = pos - _start;//计算一下扩容前pos与_start直接的距离以便以计算新pos
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;//得到新pos的位置
}
//移动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = x;
_finish++;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos <= _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
begin++;
}
_finish--;
return pos;
}
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
扩容
//size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
size_t sz = size();
//T* tmp = new T[newCapacity];
//if (_start)
//{
// memcpy(tmp, _start, sizeof(T) * size());
// delete[] _start;
//}
_start = tmp;
_finish = _start + sz;
//_finish = tmp + size();
//_start = tmp;
//_endofstorage = _start + newCapacity;
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
_finish++;
}
void pop_back()
{
assert(_finish > _start);//检查是否为空
_finish--;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}