纵然千万数据流逝,唯独vector长存
公主请阅
- 1.vector的一些方法
- 1vector和string
- push_back 插入以及三种遍历数组的方式
- 一些方法
- vector中的一些常见的方法
- 1. push_back()
- 2. pop_back()
- 3. size()
- 4. clear()
- 5. empty()
- 6. resize()
- 7. insert()
- 8. erase()
- 9. at()
- 10. front和 back()
- 11. data()
- 12. capacity()
- 13. shrink_to_fit()
- 4. swap()
- 2.vector的相关题目
- 关于vector-vector--int的解释
- 基本用法和特点
- 应用场景
- 示例代码
- 总结
- 1.只出现一次的数字
- 2.杨辉三角
- 3.vector的模拟实现
1.vector的一些方法
1vector和string
vector插入字符直接就是push_back了,没有append了
string的数组和vector的char类型有什么区别呢?
string s1;vector<char> vs;//都是字符串数组,那么存在区别吗?/*第一我们string的char数组结尾存在'\0'vector<char> vs;是不存在'\0'的概念的因为我们的vector不仅仅只存char类型,还存储其他的类型的数据
第一我们string的char数组结尾存在’\0’
vector vs;是不存在’\0’的概念的
因为我们的vector不仅仅只存char类型,还存储其他的类型的数据
vector<string> vstr;//用string实例化一个vectorstring s1 = "凯子";vstr.push_back(s1);//直接将string对象存储在vector里面vstr.push_back("李四");//隐式类型转换//单参数构造支持隐式类型转换//我们的对象vstr的模版是一个string,然后我们传的参数会被转化成string类型的//*it赋值给e,那么就会调用string拷贝构造了,需要开空间//for (auto e : v1)//支持迭代器肯定是支持范围for 的,因为范围for的底层是迭代器//{// cout << e << " ";//}//那么我们是可以加上引用的,如果不修改的话加上constfor (const auto& e : v1)//支持迭代器肯定是支持范围for 的,因为范围for的底层是迭代器{cout << e << " ";}
push_back 插入以及三种遍历数组的方式
#define _CRT_SECURE_NO_WARNINGS 1#include<vector>
#include<iostream>
using namespace std;
int main()
{vector<int> v1;vector<int> v2(10, 1);//构造10个1//插入v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);//遍历---利用下标for (size_t i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;//遍历---迭代器vector<int>::iterator it1 = v1.begin();//第一个位置的迭代器//任何类型的迭代器都在类域里面,所以我们需要指定类域while (it1 != v1.end()){cout << *it1 << " ";++it1;}cout << endl;//范围forfor (auto e : v1)//支持迭代器肯定是支持范围for 的,因为范围for的底层是迭代器{cout<<e << " ";}return 0;
}
一些方法
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;void tets_vector1()
{vector<int> v1 = { 1,2 ,3,4,5,6 };//用花括号进行初始化操作vector<int> v2({ 1,2 ,3,4,5,6 });//隐式类型转换//两种初始化的逻辑是不一样的,但是都会调用构造函数for (auto e : v1){cout << e << " ";}cout<<endl;v1.assign({ 10,20,30 });//赋值v1.insert(v1.begin(), 9);//从第一个位置那里插入一个9for (auto e : v1){cout << e << " ";}cout << endl;v1.insert(v1.begin()+2, 200);//从第一个位置那里插入一个9for (auto e : v1){cout << e << " ";}cout << endl;}int main()
{tets_vector1();return 0;
}
vector中的一些常见的方法
在C++标准模板库(STL)中,vector
是一种非常常用的动态数组容器。它提供了许多便捷的方法来操作和管理元素。下面是一些常见的 vector
方法,以及它们的用法和示例代码:
1. push_back()
-
功能:在
vector
的末尾添加一个元素。 -
示例:
std::vector<int> v;
v.push_back(10); // 向 v 添加元素 10
v.push_back(20); // 向 v 添加元素 20
2. pop_back()
-
功能:删除
vector
末尾的元素。 -
示例:
v.pop_back(); // 删除最后一个元素
3. size()
-
功能:返回
vector
中的元素数量。 -
示例:
int n = v.size(); // 获取当前元素数量
4. clear()
-
功能:清空
vector
中的所有元素,但不释放内存。 -
示例:
v.clear(); // 清空 v
5. empty()
-
功能:检查
vector
是否为空。 -
示例:
if (v.empty()) {// 处理空 vector 的情况
}
6. resize()
-
功能:调整
vector
的大小。 -
示例:
v.resize(5); // 调整 vector 大小为 5
7. insert()
-
功能:在指定位置插入元素。
-
示例:
v.insert(v.begin() + 1, 15); // 在第二个位置插入 15
8. erase()
-
功能:删除指定位置的元素。
-
示例:
v.erase(v.begin() + 1); // 删除第二个位置的元素
9. at()
-
功能:访问指定位置的元素,带越界检查。
-
示例:
int val = v.at(1); // 获取第二个元素的值
10. front和 back()
-
功能:分别访问第一个和最后一个元素。
-
示例:
int first = v.front(); // 获取第一个元素
int last = v.back(); // 获取最后一个元素
11. data()
-
功能:返回指向
vector
内部数组的指针,可以用于与 C 风格的数组互操作。 -
示例:
int* ptr = v.data(); // 获取指向内部数组的指针
12. capacity()
-
功能:返回
vector
的容量,即无需扩容时可以容纳的元素数量。 -
示例:
int cap = v.capacity(); // 获取容量
13. shrink_to_fit()
-
功能:释放多余的容量,使
vector
的容量等于当前元素数量。 -
示例:
v.shrink_to_fit(); // 调整容量大小
4. swap()
-
功能:交换两个
vector
的内容。 -
示例:
std::vector<int> v2 = {5, 6};
v.swap(v2); // 交换 v 和 v2 的内容
2.vector的相关题目
关于vector-vector–int的解释
vector<vector<int>>
是一个二维的动态数组,可以理解为一个“向量的向量”或“嵌套的向量”。这种结构通常用于表示一个矩阵或表格,每个元素本身是一个整数向量,即 vector<int>
,而外层向量则包含多个整数向量。
基本用法和特点
- 定义:
std::vector<std::vector<int>> matrix;
这里,matrix
是一个二维向量,每个元素都是一个 vector<int>
,因此可以用它来存储多行数据。
- 初始化:
你可以直接初始化它的大小和内容。
int rows = 3, cols = 4;
std::vector<std::vector<int>> matrix(rows, std::vector<int>(cols, 0));
上述代码创建了一个 3x4
的二维数组,所有元素初始化为 0
。
- 访问元素:
使用双重下标来访问二维向量中的元素。
matrix[1][2] = 5; // 设置第二行第三列的元素为 5
int val = matrix[1][2]; // 获取第二行第三列的元素
- 动态添加行和列:
-
可以使用
push_back()
为matrix
添加新的一行。
std::vector newRow = {1, 2, 3};
matrix.push_back(newRow); // 添加一行
- 对于行内的元素,使用嵌套的 `push_back()` 进行动态扩展。```C++
matrix[0].push_back(6); // 在第一行末尾添加元素 6
- 迭代遍历:
使用嵌套的for
循环来遍历整个二维向量。
for (int i = 0; i < matrix.size(); i++) {for (int j = 0; j < matrix[i].size(); j++) {std::cout << matrix[i][j] << " ";}std::cout << std::endl;
}
应用场景
-
矩阵运算:
vector<vector<int>>
很适合表示矩阵,并且可以直接应用于矩阵的加法、乘法等操作。 -
图的邻接矩阵:在图的算法中,常用
vector<vector<int>>
来表示邻接矩阵,用于存储图的边和权重。 -
动态表格数据:比如需要动态调整行列数的数据表格。
示例代码
以下是一个简单的使用示例,初始化一个 2x3
的矩阵,并对其进行赋值和输出:
#include <iostream>
#include <vector>int main() {std::vector<std::vector<int>> matrix(2, std::vector<int>(3, 0)); // 初始化2x3矩阵// 设置值matrix[0][0] = 1;matrix[0][1] = 2;matrix[0][2] = 3;matrix[1][0] = 4;matrix[1][1] = 5;matrix[1][2] = 6;// 输出矩阵for (int i = 0; i < matrix.size(); i++) {for (int j = 0; j < matrix[i].size(); j++) {std::cout << matrix[i][j] << " ";}std::cout << std::endl;}return 0;
}
输出:
1 2 3
4 5 6
总结
vector<vector<int>>
是一个非常灵活的数据结构,它允许存储和操作二维数据,且具有动态扩展的能力。无论在矩阵计算还是图表示中,它都是一种便捷且高效的选择。
1.只出现一次的数字
https://leetcode.cn/problems/single-number/description/
class Solution {
public:int singleNumber(vector<int>& nums){int val=0;for(auto e:nums){val^=e;}return val;}
};
^: 异或操作可以直接比较两个整数的对应位。当两个整数的对应位相同时,异或结果为0;当对应位不同时,结果为1。
通过范围for实现类异或操作
2.杨辉三角
https://leetcode.cn/problems/pascals-triangle/description/
/*
vector<vector<int>>是一个二维数组*/class Solution
{
public:vector<vector<int>> generate(int numRows){vector<vector<int>>vv(numRows,vector<int>());for(size_t i=0;i<numRows;++i){vv[i].resize(i+1,1);//开辟空间,地0行开辟1个,第1行开辟i+1个,多开一个,每个数据给成1}for(size_t i=0;i<vv.size();++i)//多少行,就是获取有多少个vector int的对象{for(size_t j=1;j<vv[i].size()-1;++j)//一行多少个{vv[i][j]=vv[i-1][j]+vv[i-1][j-1];//当前的位置的值是上一行的两个相加}}return vv;}
};
这个代码实现了杨辉三角(Pascal’s Triangle)。generate
函数接受一个整数 numRows
,并返回一个包含前 numRows
行杨辉三角的二维向量。以下是代码的关键部分解析:
- 初始化:
- 创建一个二维向量
vv
类型为vector<vector<int>>
,用于存储杨辉三角的各行。
- 外层循环 (第 12-14 行):
- 对于每一行
i
,将vv[i]
的大小调整为i + 1
,为每行在杨辉三角中的元素预留空间。
- 每行的第一个元素 (第 15 行):
- 每行的第一个元素
vv[i][0]
被设为 1(代码中没有显式展示,但通常是通过初始化完成的,这也是杨辉三角的基本规则)。
- 内层循环 (第 17-22 行):
- 对于每一行
i
,对于行中的每个位置j
(除了首尾元素),将vv[i][j]
设为其上一行对应的两个元素之和(即vv[i-1][j-1] + vv[i-1][j]
),根据杨辉三角的递推性质生成值。
- 返回值 (第 25 行):
- 返回填充完成的二维向量
vv
,包含了杨辉三角的各行。
3.vector的模拟实现
vector.h
#pragma once
#include<iostream>
#include<assert.h>namespace kai
{template<class T>class vector{public:/*typedef T* iterator;:定义了一种普通的迭代器类型,iterator 实际上是一个指向元素类型 T 的指针 T*。这种迭代器允许修改容器中的元素。typedef const T* const_iterator;:定义了一种常量迭代器类型,const_iterator 是一个指向 const T 的指针 const T*。
这种迭代器用于只读访问容器中的元素,不允许修改元素的值。*/typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}vector()//无参默认构造,使用空指针进行初始化操作{}//类模版的成员函数,也可以是一个函数模版template<class InputIterator>vector(InputIterator first, InputIterator last){while (first!=last){push_back(*first);++first;}}/*这段代码为 vector 实现了基于迭代器范围的初始化功能,允许从其他容器或数组中复制数据到 vector 中*///vector 类的构造函数,用于根据指定的大小和初始值来填充 vectorvector(int n, const T& val = T())//没给参数的话就用T这个类型的缺省值进行构造{reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}vector(initializer_list<T> il){reserve(il.size());//提前将空间开出来for (auto& e : il){push_back(e);}}//v2(v1)//用v1拷贝v2vector(const vector<T>& v)//v就是v1,this就是v2{reserve(v.capacity());//开一个和v1一样大的空间for (auto&e:v){push_back(e);//完成了深拷贝了}//遍历v1,将v1的值都push_back到v2里面去}void swap(vector<T>& tmp){//将指针进行交换了就行了std::swap(_start, tmp._start);std::swap(_finish, tmp._finish);std::swap(_endofstorage, tmp._endofstorage);}//v1=v3---v3赋值给v1vector<T>&operator=(vector <T>v){//我们用传值的方式将v3传给v,调用拷贝构造,那么v就有着和v3一样大的空间一样大的值了//那么我们再将v和v1进行一个交换就行了swap(v);//我们这里的this指针指向我们的v1return *this;}~vector()//析构函数{delete[] _start;_start = _finish = _endofstorage = nullptr;}T&operator [](size_t i)//返回第i个位置的引用{assert(i < size());return _start[i];}const T& operator [](size_t i)const {assert(i < size());return _start[i];}size_t size() const//获取数组元素个数{return _finish - _start;}size_t capacity()const{return _endofstorage - _start;}void resize(size_t n,T val=T())//我们这里的缺省值由于不能确定这个T的类型,我们直接在里面写一个无参构造就行了{//像这种自定义类型需要搞给到缺省值的话,那么我们就需要给到一个匿名对象了//如果T是int的话,那么我们就去调用默认构造,是什么类型的数据就去调用对应的默认构造就行了//vector、string其他的类型//内置类型的默认构造,如果是int的话就是构造成0,指针的话就构造成空//下面就是我们的内置类型的构造//int j=int()//int j=int(1)if (n < size())//小于我们的size的话我们直接进行删除操作{_finish = _start + n;//重新定义_finish,间接删除了一些元素}else//就是n大于我们的元素个数,就是说我们是需要进行数据的插入操作的{reserve(n);while (_finish < _start + n)//进行数据的插入{*_finish = val;++_finish;}}}void reserve(size_t n){if (n > capacity()){size_t oldSize = size();//提前进行记录我们的oldSizeT* tmp = new T[n];//获取n个大小的空间if (_start){//memcpy其实是浅拷贝// 我们这里的话如果将_capacity和_size拷贝过去是没问题的,但是如果是_start呢?那么就会出问题了//memcpy(tmp, _start, sizeof(T) * oldSize);//将start指向的旧空间的数据拷贝过来//将memcpy改成for循环赋值拷贝for (size_t i = 0; i < oldSize; i++){tmp[i] = _start[i];//利用赋值重载拷贝//将一个存在的对象赋值给另外一个存在的对象}delete[]_start;//释放旧空间的数据}/*如果 _start 不为空(表示当前容器中有数据),就通过 memcpy(tmp, _start, sizeof(T) * oldSize); 将旧空间的数据拷贝到新分配的空间 tmp 中。然后通过 delete[] _start; 释放旧的空间,避免内存泄漏。*///tmp是新空间的开始位置_start = tmp;//指向新空间_finish = _start + oldSize;//更新start之前我们就将oldsize记录好了_endofstorage = _start + n;//空间到N个了,size不变}}void push_back(const T& x)//插入数据{//if (_finish == _endofstorage)//没空间了//{// //如果空间是0的话我们给到4个大小的空间,如果不是0的话就扩容2倍// reserve(capacity()==0?4:capacity()*2);//扩容操作//}//*_finish = x;//++_finish;insert(_finish, x);//利用我们写好的insert进行数据的尾插操作}bool empty(){return _start == _finish;}void pop_back()//尾删{assert(!empty());//不为空--_finish;}/*迭代器失效(像野指针一样),迭代器就是一个像指针一样的东西,也可能是一个自定义类型的迭代器失效,本质是因为一些原因,迭代器不可用我们这里的insert里面的pos可能会成为野指针,如果我们不进行pos的更新的话*/
/*
我们这里插入的话第一步是进行扩容的操作
开一块空间,将数据拷贝到新空间,然后将旧空间进行释放的操作
但是我们的pos还是指向原先的旧空间的,所以我们要在内部解决迭代器失效的问题
更新pos
在扩容前计算出pos和start的相对距离
然后就可以更新了
*/void insert(iterator pos, const T& x)//在pos位置插入一个数据{assert(pos >= _start && pos <= _finish);if (_finish == _endofstorage)//没空间了{//出现扩容我们就需要将这个pos进行更新操作size_t len = pos - _start;//算下相对距离//如果空间是0的话我们给到4个大小的空间,如果不是0的话就扩容2倍reserve(capacity() == 0 ? 4 : capacity() * 2);//扩容操作pos = _start + len;//更新pos}//如果这里不更新pos的话会出现迭代器失效的问题,扩容的pos指向旧空间iterator i = _finish - 1;//i是个指针while (i >= pos)//pos后面的数据往后挪动{*(i + 1) = *i;--i;}*pos = x;++_finish;}iterator erase(iterator pos)//删除pos位置的数据{assert(pos >= _start);assert(pos < _finish);//开始挪动数据,将pos位置后面的数据往前挪动iterator i = pos + 1;while (i < _finish){*(i - 1) = *i;++i;}_finish--;return pos;//这里我们是不能返回i的,返回的是pos}private://我们直接将初始化列表去掉,给上缺省值就行了iterator _start=nullptr;//开始位置iterator _finish = nullptr;//最后一个数据的下个位置iterator _endofstorage = nullptr;//指向空间的结束位置};void test_vector1(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (size_t i = 0; i < v1.size(); i++){cout<< v1[i] << " ";}cout << endl;for (auto e : v1)//范围for底层是迭代器,我们需要将迭代器先关的功能写出来{cout << e << " ";}cout << endl;v1.pop_back();vector<int>::iterator it1 = v1.begin();while (it1!=v1.end()){cout<<*it1 << " ";++it1;}}void test2(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.insert(v1.begin() + 2, 30);//在第30个位置进行数据的插入操作for (auto e : v1)//范围for底层是迭代器,我们需要将迭代器先关的功能写出来{cout << e << " ";}cout << endl;vector<int> v2 = { 1,2,3,4,5,6 };for (auto e : v2){cout << e << " ";}cout << endl;int x;cin >> x;auto it = find(v1.begin(), v1.end(), x);if (it != v1.end()){//it是否失效?v1.insert(it, 10 * x);//对于it来说的话,形参的改变改变不了实参cout << *it << endl;//通过打印我们知道这个it已经是失效了}for (auto e : v1){cout << e << " ";}cout << endl;}void test3(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);//v1.insert(v1.begin() + 2, 30);//在第30个位置进行数据的插入操作for (auto e : v1)//范围for底层是迭代器,我们需要将迭代器先关的功能写出来{cout << e << " ";}cout << endl;//vector<int> v2 = { 1,2,3,4,5,6 };//for (auto e : v2)//{// cout << e << " ";//}//cout << endl;//int x;//cin >> x;//auto it = find(v1.begin(), v1.end(), x);//if (it != v1.end())//{// //it是否失效呢?// v1.erase(it);// //第二种失效,导致数据挪动,it已经不是指向指向位置了// //it也失效了,it已经不是指向之前的位置了,可能会导致逻辑问题// //vs强制检查,失效迭代器不让你访问//}//for (auto e : v1)//{// cout << e << " ";//}//cout << endl;//要求删除所有偶数auto it = v1.begin();while (it != v1.end()){if (*it % 2 == 0){v1.erase(it);++it;}}for (auto e : v1){cout << e << " ";}cout << endl;}void test4(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1)//范围for底层是迭代器,我们需要将迭代器先关的功能写出来{cout << e << " ";}cout << endl;//要求删除所有偶数auto it = v1.begin();while (it != v1.end()){if (*it % 2 == 0){//进行it的更新it=v1.erase(it);//删除后,erase回返回删除位置的下一个位置//失效的迭代器更新之后我们再进行访问}//vs强制检查,只要我们访问erase后面的迭代器就会报错else{++it;}}for (auto e : v1)//范围for底层是迭代器,我们需要将迭代器先关的功能写出来{cout << e << " ";}cout << endl;}void test5(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.resize(10);for (auto e : v1)//1 2 2 3 4 0 0 0 0 0{cout << e << " ";}cout << endl;cout << endl;v1.resize(15,1);for (auto e : v1)//1 2 2 3 4 0 0 0 0 0 1 1 1 1 1{cout << e << " ";}cout << endl;v1.resize(2);for (auto e : v1)//1 2{cout << e << " ";}cout << endl;vector<int> v2(v1);//拷贝构造出一个vector出来//这里仅仅是值拷贝---浅拷贝for (auto e : v2)//1 2{cout << e << " ";}cout << endl;vector<int> v3 = { 10,20,30,40 };//赋值---浅拷贝是会报错的v1 = v3;for (auto e : v1)//1 2{cout << e << " ";}cout << endl;}void test6(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(2);v1.push_back(3);v1.push_back(4);vector<int> v2(v1.begin(), v1.end());
/*
* 这段代码为 vector 实现了基于迭代器范围的初始化功能,
允许从其他容器或数组中复制数据到 vector 中
*/for (auto e : v1)//1 2{cout << e << " ";}cout << endl;string s1("hello");vector<int> v3(s1.begin(), s1.end());for (auto e : v3)//1 2{cout << e << " ";}cout << endl;/*这里定义了一个字符串 s1,内容为 "hello"。然后试图使用 s1 的起始和结束迭代器来初始化一个 vector<int> 类型的 v3。由于字符串的迭代器返回的是字符类型(char),而 v3 是 int 类型的 vector,编译器会将 char 自动转换为 int,因此每个字符的 ASCII 值将被存入 v3 中。这种做法的效果是将字符串 "hello" 转换为一个 vector<int>,其中每个字符的 ASCII 值分别存储在 v3 中。*/vector<int> v7(10,1);//用10个1进行初始化操作for (auto e : v7){cout << e << " ";}cout << endl;}}