C++ vector 全面解析:从基础用法到深度剖析----《Hello C++ Wrold!》(15)--(C/C++)_c++代码,vector<fractal> identifyfractals(const vecto
文章目录
- 前言
- 构造vector的几种方法
- vector的迭代器
- vector的空间操作
- vector获取位置
- vector的增删查改
- vector的模拟实现
-
- 源代码的看法:
- 模拟实现
- 作业部分
前言
在 C++ 标准模板库(STL)中,vector 是最常用也最灵活的容器之一。它作为动态数组,既保留了数组随机访问的高效性,又具备动态扩容的灵活性,在实际开发中有着广泛的应用。
本文将从 vector 的构造方法入手,逐步深入到迭代器使用、空间管理、元素操作等核心知识点,不仅会讲解各种 API 的用法细节,还会通过模拟实现代码帮助读者理解其底层工作原理。同时,针对 vector 使用中常见的迭代器失效、边界访问等问题进行详细说明,并结合典型算法题目展示 vector 在实际场景中的应用,旨在为读者提供一份系统、全面的 vector 学习指南,无论是 C++ 初学者还是需要巩固基础的开发者,都能从中获得实用的知识与技巧。
构造vector的几种方法
具体的使用方法查询官网
像这种网站的容器里有看不懂的类型,一般就是typedef出来的
示例:(举的中间那两个的)
vector v1(10, 1);
vector v3(v1.begin(), v1.end());
eg:vector<vector>的用法 vector<vector> vv; vv.size(); vv[i].size();resize时,vv要resize,vv[i]也要resize
vector的迭代器
begin--获取第一个字符的迭代器end--获取最后一个字符的下一个位置的迭代器rbegin--获取最后一个字符的迭代器rend-获取第一个字符的上一个位置的迭代器(这里的迭代器其实是反向迭代器)反向迭代器用范围for的话是倒着遍历的注意:vector的迭代器不一定是原生指针注意:反向迭代器++和迭代器++的移动方向是相反的
vector的空间操作
size–获取数据个数
capacity–获取的容量大小
empty–判断是否为空
resize–改变vector的size和capacity
reserve–改变vector的capacity
注意:[]去访问的时候是根据size来的比如 a是vector类型的 a.capacity() == 10;a.size() == 1a[9]就是越界的,会报错
vector获取位置
operator[]--一般用这个front --返回容器中第一个元素的引用back --返回容器最后一个元素的引用data(C++11允许)--获取vector内部存储元素的连续内存的首地址
vector的增删查改
push_back 尾插
pop_back 尾删
–上面这两感觉没啥用
insert
注意:传的是迭代器 插入的话,是在pos前面插入
erase 删除position位置的数据(这个地方没数据的话,会报错)
返回值:返回一个迭代器,指向被删除元素的下一个元素
swap 交换两个vector的数据空间
operator[]
emplace – 以后再了解,涉及右值引用了
注意:vector里面没有find,但是里面有find可以用
里面find的用法:
查找的是第一个出现val的位置
注意:这种区间都是左闭右开(迭代器和指针)
vector的模拟实现
源代码的看法:
1.通过单词意思去猜测成员的作用
2.或者通过构造函数和插入接口函数(比如:insert)来看成员作用
3.先看大框架
模拟实现
namespace renshen{template<class T>class vector{public: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(size_t n, const T& val = T())//注意理解这里的T() :_start(nullptr), _finish(nullptr), _endofstorage(nullptr){resize(n, val);}template<class InputIterator>//模板里面可以套模板vector(InputIterator first, InputIterator last) :_start(nullptr), _finish(nullptr), _endofstorage(nullptr){while (first != last){push_back(*first);++first;}}vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}vector(const vector<T>& v)//传统写法 :_start(nullptr), _finish(nullptr), _endofstorage(nullptr){_start = new T[v.capacity()];//size也行//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();_endofstorage = _start + v.capacity();//前面是size的话,这也要改成size}/*vector(const vector& v)//现代写法:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(v.capacity());for (auto e : v){push_back(e);}}*/void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * sz); for (size_t i = 0; i < sz; i++) {tmp[i] = _start[i]; }delete[] _start;//把那一块都删了}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void resize(size_t n, const T& val = T()){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = val;++_finish;}}}void push_back(const T& x){insert(end(), x);}void pop_back(){erase(--end());}size_t capacity() const{return _endofstorage - _start;}size_t size() const{return _finish - _start;//不用打括号}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}--_finish;return pos;}private:iterator _start;iterator _finish;//最后一个元素的下一个位置iterator _endofstorage;//内存的末尾的下一个位置};void print(const vector<int>& v){for (auto e : v)//前面有const_iterator类型的begin和end,这里const的vector才能用范围for{cout << e << \" \";}cout << endl;}
问题:以下两个分别调用的哪一个
vector v(10, 1);–编译器会去调用迭代器区间那个,因为另外一个是size_t,没有那个合适–10的话编译器默认是int类型
vector v1(10, “1111”);–调用的是size_t那个
引申:迭代器失效:1.常常出现在insert之后(因为扩容过后,迭代器就成’‘野指针’\'了)
2.erase也有这种情况--后面用形参迭代器VS会强制报错
总结:vector的erase和insert形参迭代器不再去访问了
代码要讲究通用性,自己写的代码要VS下能用,gcc也要能用–不用编译器的特性就行了
reverse和vecor的构造函数里面最好不用那个memcpy–这个是浅拷贝,但是遇到比如:string这种的时,就不行了
eg:int j = int();这样也是对了的,这样j就是int的默认值了-0
作业部分
只出现一次的数字 力扣
只出现一次的数字 力扣方法:用异或的方法注意:异或是符合分配律的
代码参考:class Solution {public: int singleNumber(vector<int>& nums) { int e =0 ; for(int i = 0;i<nums.size();i++) { e = e^nums[i]; } return e; }};
T是一个数据类型,在vs系列编译器中,debug模式下关于std::vector::at 和 std::vector::operator[] 描述正确的是(C)A.at总是做边界检查, operator[] 不做边界检查.B.at 不做边界检查, operator[] 做边界检查.C.at和operator[] 都是会做边界检查的D.以上都不对
下面程序的输出结果正确的是(C)int main(){int ar[] = {1,2,3,4,5,6,7,8,9,10};int n = sizeof(ar) / sizeof(int);vector<int> v(ar, ar+n);cout<<v.size()<<\":\"<<v.capacity()<<endl;v.reserve(100);v.resize(20);cout<<v.size()<<\":\"<<v.capacity()<<endl;v.reserve(50);v.resize(5);cout<<v.size()<<\":\"<<v.capacity()<<endl;}A.10:10 20:100 5:50B.10:20 20:100 5:100C.10:10 20:100 5:100D.10 10 20:20 20:50//总结:容量不会变小,但是size会变小
这种情况的话,其实算是值返回,会深拷贝,所以没事
电话号码的字母组合 力扣
电话号码的字母组合 力扣注意:vector a; 没有值的时候,直接返回a,不要返回\"\" 还有,dfs传a过去要传a的引用eg:a.pushback(\" \")的话,不是在已经有的string后面追加(注意:vector没有+=,而是用的pushback)而是比如本为a[0],现在加在a[1]string为空也不能跟nullptr比较
代码展示:class Solution {public: string qq[10] = {\"\",\"\",\"abc\",\"def\",\"ghi\",\"jkl\",\"mno\",\"pqrs\",\"tuv\",\"wxyz\"}; void dfs(vector<string>&f,int level,string p,string digits) { if(level == digits.size()) { f.push_back(p); return; } for(int i = 0;i<qq[digits[level]-\'0\'].size();i++) { dfs(f,level+1,p+qq[digits[level]-\'0\'][i],digits); } } vector<string> letterCombinations(string digits) { vector<string> a; if(digits == \"\") return a; dfs(a,0,\"\",digits); return a; }};
牛客网 数组中出现次数超过一半的数字
牛客网 数组中出现次数超过一半的数字做法:其实就是排序之后,数组中间的那个数(如果数组长度是偶数的话,中间那俩个数其实都是那个数)下标就是numbers.size()/2
代码展示:class Solution {public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param numbers int整型vector * @return int整型 */ int MoreThanHalfNum_Solution(vector<int>& numbers) { sort(numbers.begin(),numbers.end()); return numbers[numbers.size()/2]; }};
力扣 只出现一次的数ii
力扣 只出现一次的数ii --这道题非常薄弱--自己对二进制不熟!!!做法:对于出现3次的数字,二进制各位出现的次数都是3的倍数,因此对统计的为1的二进制位各位%3 结果就是只出现一次的数字注意:线性时间复杂度的意思是时间复杂度要是O(N)引申:sort的时间复杂度是O(NlogN)
代码展示:class Solution {public: int singleNumber(vector<int>& nums) { int ret = 0; for (int i = 0; i < 32; ++i) { int total = 0; for (int e: nums) { total += ((e >> i) & 1); } if (total % 3) { ret |= (1 << i); } } return ret; }};
力扣 只出现一次的数iii
力扣 只出现一次的数iii--这个题也是薄弱项做法:先全部异或到sum上,找出sum二进制是1的最低位(第i位) 假设num1 = num2 = 0;把第i位是1的数全异或到num1上,把第i位是0是全异或到num2上即可这个题要注意异或和要用long long int存,不然会溢出不能写成 int num1 = num2 = 0; num1^=e;(^=之间不能有空格)
代码展示:class Solution {public: vector<int> singleNumber(vector<int>& nums) { long long int sum = 0; for(auto e: nums) { sum = sum^e; } long long int lowbit = sum&(-sum); int num1 = 0,num2 = 0; for(auto e:nums) { if((lowbit&e) == 0) num1^= e; else num2^=e; } return {num1,num2}; }};