目录
vector的使用
首先vector是一个长度可以变化的顺序表,类似string是一个写好的数据结构
vector的迭代
// vector的使用
void test_vector1() {
vector<int> v1;
vector<int> v2(10, 0);
v1 = v2;
vector<int> v3(v2.begin(), v2.end());
for (size_t i = 0; i < v1.size(); i++) {
cout << v1[i];
}
cout << endl;
vector<int>::iterator it = v2.begin();
// auto it = v2.begin();
while (it != v2.end()) {
cout << *it;
it++;
}
cout << endl;
}
vector需要注意的点
resize和reserve
void test_vector2() {
// 关于vector中的resize 和 reserve
vector<int> v;
// v.reserve(100); // size = 0 capacity = 100
v.resize(100); // size = 100 capacity = 100
// 当为reserve(100)时仅仅开了空间,size为0,无法实现
for (size_t i = 0; i < v.size(); i++) {
v[i] = i;
cout << v[i] << ' ';
}
cout << endl;
cout <<"vector<int> max_size(): " << v.max_size() << endl;
}
find方法
我们在查阅vector文档时,发现vector没有像string那样子重载find方法,带着疑惑我们了解到vector使用了标准库中的全局find方法。在研究会我们发现,string对应的是字符串对象,而vector<类型名>随着类型的改变而改变存储的数据类型,也就是既可以存储数字,也可以存储字符串,那么就需要用模版来实现多种数据类型的find,因此就使用全局find,语法也与string有差异
void test_vector3() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(1);
v.insert(v.begin(), 6);
v.insert(v.end(), 6);
for (size_t i = 0; i < v.size(); i++) {
cout << v[i] << ' ';
}
cout << endl;
// 通过迭代器查找位置
auto it = find(v.begin(), v.end(), 2);
// 查找到插入
if (it != v.end()) {
v.insert(it, 3);
// v.insert(it + 1, 3); 不知道为什么这里多加这一句会报错,两句好像只能有一句
}
for (size_t i = 0; i < v.size(); i++) {
cout << v[i] << ' ';
}
cout << endl;
// insert 和 erase 使用完后会导致迭代器失效,需要再次查找迭代器位置
it = find(v.begin(), v.end(), 3);
// v.erase(it) 就只是删掉it这个位置
v.erase(it,v.end());
for (size_t i = 0; i < v.size(); i++) {
cout << v[i] << ' ';
}
cout << endl;
}
这里的代码块中我们也要注意,用find通过迭代器,进行增删(insert erase)操作后迭代器会失效,这一部分的内容在后续知识体系会讲解
vector实现二维数组
二维数组是由一维数组嵌套而成的,如果一个数组的每个元素又是一个数组,那么它就是二维数组。在C语言中,一个二维数组可以分解为多个一维数组,并且这些一维数组可以直接拿来使用。每个一维数组有自己的索引,可以通过索引来访问二维数组中的元素
在讲vector实现二维数组之前,我们先讲一下c语言中实现二维数组
除了通过直接定义二维数组,我们也可以通过指针数组来实现二维数组,实际上一般我们通过指针数组来进行,因为需要调用函数
int* array; // 对应着一维数组,放着行
int** aArray; // 对应着二维数组,里面放着列的地址
// 通过malloc我们就可以看出
array = (int*)malloc(sizeof(int)*一维数组的长度);
aArray = (int**)malloc(sizeof(int*)*某个长度下标一维数组的个数);
接下来进行就通过双层循环来遍历赋值给二维数组,这里我们发现对于c语言的二维数组还是特别麻烦,不过我们学习了高级语言cpp那么接下来就是cpp的场子
上面我们说过vector是一个写好的数据结构,那么我们从底层来分析
如果现在还有已获得话那就借助一道杨辉三角来收尾解答
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv;
vv.resize(numRows);
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;
}
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-1] + vv[i-1][j];
}
}
}
return vv;
}
};
vector的实现
与string类似,所以我们就缩减篇幅直接进行vector的实现
如图 与传统的_array,_size,_capacity不同,vector用的是三个指针(迭代器)_start,_finish, _end_of_storage,来实现功能,本质上两种实现是一致的
vector的框架
template<class T>
class my_vector {
public:
typedef T* iterator; // iterator表示某个类型的指针
typedef const T* const_iterator;
private:
// 数组的头, 相当于 type* _array
iterator _start;
// 数组有效数据的尾巴, 相当于 _array + _size
iterator _finish;
// 数组空间的尾巴, 相当于_array + _capacity
iterator _end_of_storage;
};
vector的长度与容量
我们知道通过指针间的减法,可以获得这两个指针间的有效数据个数,所以就能获得_size,_capacity,而_start本质上就是_array
// vector的返回参数
size_t capacity() const { // vector的容量
return _end_of_storage - _start;
}
size_t size() const { // 返回vector的大小
return _finish - _start;
}
除了长度和容量的表示,我们大多时候还要面对顺序表长度的改变,以及容量的扩大
void reserve(size_t n) { // 扩容函数
if (n > capacity()) {
size_t _size = size();
T* tmp = new T[n];
if (_start) {
// 手动拷贝
for (size_t i = 0; i < _size; i++) {
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + _size; // 这里有个隐藏的坑,_size这个变量取消,然后换size()
_end_of_storage = _start + n;
}
}
void resize(size_t n, const T& data = T()) { // 大小重置
if (n <= size())
_finish = _start + n;
else {
reserve(n); // 借助reserve()判断大小大于容量的话会扩容,小于容量不会扩容
while (_finish < _start + n) {
*_finish = data;
_finish++;
}
}
}
看这块代码块,有一个隐藏的坑,如果取消了_size这个临时变量
那么因为 size() 这个函数在 _finish = _start + size(); 中,_start已经改变了,而原来的_finish没有改变,并且已经不是原来同一个数组的头尾地址,导致这样子运算最终报错
vector的构造与析构函数
my_vector()
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
}
my_vector(const my_vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(v.capacity());
for (auto& e : v) { // 将数据插入
push_back(e);
}
};
template<class inputIterator>
my_vector(inputIterator first, inputIterator end) { // 通过迭代器拷贝构造某一部分
while (first != end) {
push_back(*first);
first++;
}
}
my_vector(int n, const T& data = T()) { // 防止传入的为(10,0)调用模版(上一个构造函数)
reserve(n);
for (int i = 0; i < n; i++) {
push_back(data);
}
}
my_vector(size_t n, const T& data = T()) {
reserve(n);
for (size_t i = 0; i < n; i++) {
push_back(data);
}
}
~my_vector() { // 析构
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
这里着重讲一下通过迭代器来进行拷贝构造
这个就是通过类内,定义模版来实现迭代器,不过正因为是模版,inputIterator也可以为int类型,这里听得可能有点蒙,那我给个例子
my_vector<int> v1;
my_vector<int> v2;
v2 = v1(10,0);
v2 = v1(v1.begin(), v1.end())
你说v2 = v1(10,0);会不会进入这个迭代器构造函数,答案是会的,那么对于int类型,*(int类型)就会报错,解引用操作符无法对int使用。那么如何解决呢,于是乎就多引入了两个构造函数,通过更加匹配,所以编译器会主动进行相匹配的构造函数,而不进入迭代器构造函数
vector的增删
// 插入
void push_back(const T& data) { // 插入单个数据
if (_finish == _end_of_storage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = data;
_finish++;
}
void insert(iterator postion, const T& data) {
assert(postion >= _start && postion <= _finish);
size_t pos_len = postion - _start; // 记录相对位置,防止扩容后postion失效
if (_finish == _end_of_storage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
postion = _start + pos_len; // 扩容产生新的postion
}
// 注意扩容可能导致迭代器失效
iterator flag = _finish - 1;
while (flag >= postion) {
*(flag + 1) = *flag;
flag--;
}
*postion = data;
_finish++;
}
// 删除
iterator erase(iterator postion) {
// 注意当删除了postion时可能会导致迭代器失效,即前后地址不连续,导致无法找到
// 连续使用的时候需要保留postion前后的迭代器地址
assert(postion >= _start && postion < _finish);
iterator flag = postion + 1;
while (flag < _finish) {
*(flag - 1) = *flag;
flag++;
}
_finish--;
// 这里返回了
return postion;
}
注意对于insert和erase这两个函数,通过iterator这个迭代器的过程中,会导致迭代器失效
!来自C知道的回答!
vector中的迭代器会失效的原因有以下几点:
-
添加元素:当向vector中添加元素时,如果当前容量不足,会重新分配一块更大的内存空间,并将原有的元素复制到新的内存空间中。这个过程可能会导致原有的迭代器失效,因为它们指向的内存地址已经发生变化。
-
删除元素:当从vector中删除元素时,如果删除的不是最后一个元素,会将后面的元素向前移动填补空缺,这也会导致原有的迭代器失效。
-
插入元素:当在vector中插入元素时,会将插入位置后面的元素向后移动,同样会导致原有的迭代器失效。
-
内存重新分配:当vector的容量增加时,会重新分配内存空间,并将原有的元素复制到新的内存空间中。这个过程会导致所有的迭代器失效。
简易的vector
由于现在学习的知识有限,所以我们大致做了个简单的vector
template<class T>
class my_vector {
public:
typedef T* iterator; // iterator表示某个类型的指针,作为迭代器
typedef const T* const_iterator;
// 迭代器
iterator begin() {
return _start;
}
iterator end() {
return _finish;
}
// vector的返回参数
size_t capacity() const { // vector的容量
return _end_of_storage - _start;
}
size_t size() const { // 返回vector的大小
return _finish - _start;
}
// 交换函数
void swap(my_vector<T>& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
// 判空
bool empty() const { // 为空返回非0
return _finish == _start;
}
// 重载[]
T& operator[](size_t position) {
assert(position < size());
return _start[position];
}
// 重载=
my_vector<T>& operator=(my_vector<T> v) { // 传入临时拷贝,防止被修改,用const类型就不能swap
swap(v); // 用临时拷贝来替代修改
return *this;
}
// 主要成员函数
// 构造与析构
my_vector()
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
}
my_vector(const my_vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(v.capacity());
for (auto& e : v) { // 将数据插入
push_back(e);
}
};
template<class inputIterator>
my_vector(inputIterator first, inputIterator end) { // 通过迭代器拷贝构造某一部分
while (first != end) {
push_back(*first);
first++;
}
}
my_vector(int n, const T& data = T()) { // 防止传入的为(10,0)调用模版(上一个构造函数)
reserve(n);
for (int i = 0; i < n; i++) {
push_back(data);
}
}
my_vector(size_t n, const T& data = T()) {
reserve(n);
for (size_t i = 0; i < n; i++) {
push_back(data);
}
}
~my_vector() { // 析构
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
void reserve(size_t n) { // 扩容函数
if (n > capacity()) {
size_t _size = size();
T* tmp = new T[n];
if (_start) {
// 手动拷贝
for (size_t i = 0; i < _size; i++) {
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + _size; // 这里有个隐藏的坑,_size这个变量取消,然后换size()
_end_of_storage = _start + n;
}
}
void resize(size_t n, const T& data = T()) { // 大小重置
if (n <= size())
_finish = _start + n;
else {
reserve(n); // 借助reserve()判断大小大于容量的话会扩容,小于容量不会扩容
while (_finish < _start + n) {
*_finish = data;
_finish++;
}
}
}
// 插入
void push_back(const T& data) { // 插入单个数据
if (_finish == _end_of_storage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = data;
_finish++;
}
void insert(iterator postion, const T& data) {
assert(postion >= _start && postion <= _finish);
size_t pos_len = postion - _start; // 记录相对位置,防止扩容后postion失效
if (_finish == _end_of_storage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
postion = _start + pos_len; // 扩容产生新的postion
}
// 注意扩容可能导致迭代器失效
iterator flag = _finish - 1;
while (flag >= postion) {
*(flag + 1) = *flag;
flag--;
}
*postion = data;
_finish++;
}
// 删除
iterator erase(iterator postion) {
// 注意当删除了postion时可能会导致迭代器失效,即前后地址不连续,导致无法找到
// 连续使用的时候需要保留postion前后的迭代器地址
assert(postion >= _start && postion < _finish);
iterator flag = postion + 1;
while (flag < _finish) {
*(flag - 1) = *flag;
flag++;
}
_finish--;
// 这里返回了
return postion;
}
void clear() {
_finish = _start;
}
// 查的话通过库中的find函数
T& at(size_t n) {
assert(n < size());
return _start[_start + n];
}
private:
// 数组的头, 相当于 type* _array
iterator _start;
// 数组有效数据的尾巴, 相当于 _array + _size
iterator _finish;
// 数组空间的尾巴, 相当于_array + _capacity
iterator _end_of_storage;
};
测试函数
// 测试迭代
void test_vector1() {
my_vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
v.push_back(1);
for (size_t i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
cout << endl;
my_vector<int>::iterator it = v.begin();
while (it != v.end()) {
*it = 10;
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
// 测试resize
void test_vector2() {
my_vector<int*> v1;
v1.resize(1);
my_vector<string> v2;
//v2.resize(10, string("xxx"));
v2.resize(10, "xxx");
for (auto e : v1) {
cout << e << " ";
}
cout << endl;
for (auto e : v2) {
cout << e << " ";
}
cout << endl;
}
// 测试insert
void test_vector3() {
my_vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
for (auto e : v) {
cout << e << " ";
}
cout << endl;
my_vector<int>::iterator it = v.begin() + 2;
v.insert(it, 30);
for (auto e : v) {
cout << e << " ";
}
cout << endl;
//v.insert(v.begin(), 30);
v.insert(v.begin() + 3, 30); // 85, 86行不能同时,迭代器会失效
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
// 测试erase
void test_vector4() {
my_vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
for (auto e : v) {
cout << e << " ";
}
cout << endl;
auto pos = v.begin();
v.erase(pos);
for (auto e : v) {
cout << e << " ";
}
cout << endl;
v.erase(v.begin() + 3);
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
//void test_vector5()
//{
// // 1 2 3 4 5
// // 1 2 3 4 5 6
// // 2 2 3 4 5
// std::vector<int> v;
// v.push_back(1);
// v.push_back(2);
// v.push_back(3);
// v.push_back(4);
// v.push_back(5);
// //v.push_back(6);
// for (auto e : v)
// {
// cout << e << " ";
// }
// cout << endl;
// auto it = v.begin();
// while (it != v.end())
// {
// // vs2019进行强制检查,erase以后认为it失效了,不能访问,访问就报错
// if (*it % 2 == 0)
// {
// v.erase(it);
// }
// ++it;
// }
// for (auto e : v)
// {
// cout << e << " ";
// }
// cout << endl;
//}
// 测试迭代器失效
void test_vector5() { // 与上面部分进行对比
//std::vector<int> v;
my_vector<int> v;
v.push_back(1); // 测试案例 1,2,3,4,5
v.push_back(2); // 2,3,2,2,4
v.push_back(3);
v.push_back(4);
v.push_back(4);
v.push_back(5);
v.push_back(6);
for (auto e : v) {
cout << e << " ";
}
cout << endl;
auto it = v.begin();
while (it != v.end()) {
if (*it % 2 == 0)
it = v.erase(it);
else
++it;
}
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
// 测试vector里面放string
void test_vector6() {
my_vector<string> v;
v.push_back("111111111111111111111");
v.push_back("111111111111111111111");
v.push_back("111111111111111111111");
v.push_back("111111111111111111111");
v.push_back("111111111111111111111");
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
// 测试拷贝构造和等号重载
void test_vector7() {
my_vector<int> v1;
v1.push_back(1);
v1.push_back(1);
v1.push_back(1);
v1.push_back(1);
v1.push_back(1);
my_vector<int> v2(v1);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
my_vector<int> v3;
v3.push_back(10);
v3.push_back(20);
v3.push_back(30);
v3.push_back(40);
v1 = v3;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void test_vector8() {
//vector<int> v0(10, 0);
my_vector<string> v1(10, "xxxx");
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
my_vector<int> v2;
v2.push_back(10);
v2.push_back(20);
v2.push_back(30);
v2.push_back(40);
my_vector<int> v3(v2.begin(), v2.end());
string str("hello world");
my_vector<int> v4(str.begin(), str.end());
for (auto e : v3)
{
cout << e << " ";
}
cout << endl;
for (auto e : v4)
{
cout << e << " ";
}
cout << endl;
}
最终代码
写到这里vector大致就结束了,剩下的反向迭代器以及部分的高级用法内容后续补上
#pragma once
#include<iostream>
#include<string>
#include<assert.h>
#include <initializer_list>
using namespace std;
namespace zhong{
template<class iterator, class Reference, class Pointer>
struct Reverse_iterator {
typedef Reverse_iterator<iterator, Reference, Pointer> self;
Reverse_iterator(iterator it) :_it(it){}
self& operator++() {
--_it;
return *this;
}
self& operator--() {
++_it;
return *this;
}
Reference operator*() const {
iterator cur = _it;
return *(--cur);
}
Pointer operator->() const { return &(operator*()); }
bool operator==(const self& s) { return _it == s._it; }
bool operator!=(const self& s) { return _it != s._it; }
iterator _it;
};
template<class T>
class my_vector {
public:
typedef T* iterator; // iterator表示某个类型的指针,作为迭代器
typedef const T* const_iterator;
typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;
typedef Reverse_iterator<const_iterator,const T&,const T*> const_reverse_iterator;
// 迭代器
iterator begin() { return _start; }
iterator end() { return _finish; }
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }
// 反向迭代器
reverse_iterator rbegin() { return reverse_iterator(_finish); }
reverse_iterator rend() { return reverse_iterator(_start); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(_finish); }
const_reverse_iterator rend() const { return const_reverse_iterator(_start); }
// vector的返回参数
size_t capacity() const { return _end_of_storage - _start; }
size_t size() const { return _finish - _start; }
// 交换函数
void swap(my_vector<T>& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
// 判空
bool empty() const { // 为空返回非0
return _finish == _start;
}
// 重载[]
T& operator[](size_t position) {
assert(position < size());
return _start[position];
}
// 重载=
my_vector<T>& operator=(my_vector<T> v) { // 传入临时拷贝,防止被修改,用const类型就不能swap
swap(v); // 用临时拷贝来替代修改
return *this;
}
// 主要成员函数
// 构造与析构
my_vector()
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
}
my_vector(const my_vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(v.capacity());
for (size_t i = 0; i < v.size(); i++) {
push_back(v._start[i]);
}
};
template<class inputIterator>
my_vector(inputIterator first, inputIterator end) { // 通过迭代器拷贝构造某一部分
while (first != end) {
push_back(*first);
first++;
}
}
my_vector(int n, const T& data = T()) { // 防止传入的为(10,0)调用模版(上一个构造函数)
reserve(n);
for (int i = 0; i < n; i++) {
push_back(data);
}
}
my_vector(size_t n, const T& data = T()) {
reserve(n);
for (size_t i = 0; i < n; i++) {
push_back(data);
}
}
~my_vector() { // 析构
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
void reserve(size_t n) { // 扩容函数
if (n > capacity()) {
size_t _size = size();
T* tmp = new T[n];
if (_start) {
// 手动拷贝
for (size_t i = 0; i < _size; i++) {
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + _size; // 这里有个隐藏的坑,_size这个变量取消,然后换size()
_end_of_storage = _start + n;
}
}
void resize(size_t n, const T& data = T()) { // 大小重置
if (n <= size())
_finish = _start + n;
else {
reserve(n); // 借助reserve()判断大小大于容量的话会扩容,小于容量不会扩容
while (_finish < _start + n) {
*_finish = data;
_finish++;
}
}
}
// 插入
void push_back(const T& data) { // 插入单个数据
if (_finish == _end_of_storage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = data;
_finish++;
}
void insert(iterator postion, const T& data) {
assert(postion >= _start && postion <= _finish);
size_t pos_len = postion - _start; // 记录相对位置,防止扩容后postion失效
if (_finish == _end_of_storage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
postion = _start + pos_len; // 扩容产生新的postion
}
// 注意扩容可能导致迭代器失效
iterator flag = _finish - 1;
while (flag >= postion) {
*(flag + 1) = *flag;
flag--;
}
*postion = data;
_finish++;
}
// 删除
iterator erase(iterator postion) {
// 注意当删除了postion时可能会导致迭代器失效,即前后地址不连续,导致无法找到
// 连续使用的时候需要保留postion前后的迭代器地址
assert(postion >= _start && postion < _finish);
iterator flag = postion + 1;
while (flag < _finish) {
*(flag - 1) = *flag;
flag++;
}
_finish--;
// 这里返回了
return postion;
}
void clear() {
_finish = _start;
}
// 查的话通过库中的find函数
T& at(size_t n) {
assert(n < size());
return _start[_start + n];
}
private:
// 数组的头, 相当于 type* _array
iterator _start;
// 数组有效数据的尾巴, 相当于 _array + _size
iterator _finish;
// 数组空间的尾巴, 相当于_array + _capacity
iterator _end_of_storage;
};
}