C++ vector 全面解析:从基础用法到深度剖析----《Hello C++ Wrold!》(15)--(C/C++)

C++ vector 全面解析:从基础用法到深度剖析----《Hello C++ Wrold!》(15)--(C/C++)

文章目录

前言

在 C++ 标准模板库(STL)中,vector 是最常用也最灵活的容器之一。它作为动态数组,既保留了数组随机访问的高效性,又具备动态扩容的灵活性,在实际开发中有着广泛的应用。

本文将从 vector 的构造方法入手,逐步深入到迭代器使用、空间管理、元素操作等核心知识点,不仅会讲解各种 API 的用法细节,还会通过模拟实现代码帮助读者理解其底层工作原理。同时,针对 vector 使用中常见的迭代器失效、边界访问等问题进行详细说明,并结合典型算法题目展示 vector 在实际场景中的应用,旨在为读者提供一份系统、全面的 vector 学习指南,无论是 C++ 初学者还是需要巩固基础的开发者,都能从中获得实用的知识与技巧。

构造vector的几种方法

在这里插入图片描述
具体的使用方法查询官网

像这种网站的容器里有看不懂的类型,一般就是typedef出来的

示例:(举的中间那两个的)

vector<int> v1(10, 1);vector<int> v3(v1.begin(), v1.end());
eg:vector<vector<int>>的用法 vector<vector<int>> 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<int>类型的 a.capacity() == 10;a.size() == 1 a[9]就是越界的,会报错 

vector获取位置

operator[]--一般用这个 front --返回容器中第一个元素的引用 back --返回容器最后一个元素的引用 data(C++11允许)--获取vector内部存储元素的连续内存的首地址 

vector的增删查改

push_back 尾插

pop_back 尾删

–上面这两感觉没啥用

insert



erase 删除position位置的数据(这个地方没数据的话,会报错)

返回值:返回一个迭代器,指向被删除元素的下一个元素

swap 交换两个vector的数据空间

operator[]

emplace – 以后再了解,涉及右值引用了
注意:vector里面没有find,但是里面有find可以用
里面find的用法:

查找的是第一个出现val的位置

注意:这种区间都是左闭右开(迭代器和指针)

vector的模拟实现

源代码的看法:

1.通过单词意思去猜测成员的作用

2.或者通过构造函数和插入接口函数(比如:insert)来看成员作用

3.先看大框架

模拟实现

namespace renshen {template<classT>classvector{public:typedef T* iterator;typedefconst 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<classInputIterator>//模板里面可以套模板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<T>& v)//现代写法 :_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { reserve(v.capacity()); for (auto e : v) { push_back(e); } }*/voidswap(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;}}voidreserve(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;}}voidresize(size_t n,const T& val =T()){if(n <size()){ _finish = _start + n;}else{reserve(n);while(_finish != _start + n){*_finish = val;++_finish;}}}voidpush_back(const T& x){insert(end(), x);}voidpop_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;//内存的末尾的下一个位置};voidprint(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之后(因为扩容过后,迭代器就成’‘野指针’'了)

总结:vector的erase和insert形参迭代器不再去访问了

代码要讲究通用性,自己写的代码要VS下能用,gcc也要能用–不用编译器的特性就行了
reverse和vecor的构造函数里面最好不用那个memcpy–这个是浅拷贝,但是遇到比如:string这种的时,就不行了
eg:int j = int();这样也是对了的,这样j就是int的默认值了-0 

作业部分

只出现一次的数字 力扣

只出现一次的数字 力扣 方法:用异或的方法 注意:异或是符合分配律的 
代码参考:classSolution{public:intsingleNumber(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)intmain(){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:1020:1005:50 B.10:2020:1005:100 C.10:1020:1005:100 D.101020:2020:50//总结:容量不会变小,但是size会变小
在这里插入图片描述
这种情况的话,其实算是值返回,会深拷贝,所以没事 

电话号码的字母组合 力扣

电话号码的字母组合 力扣 注意:vector<string> a; 没有值的时候,直接返回a,不要返回"" 还有,dfs传a过去要传a的引用 eg:a.pushback(" ")的话,不是在已经有的string后面追加 (注意:vector没有+=,而是用的pushback) 而是比如本为a[0],现在加在a[1] string为空也不能跟nullptr比较 
代码展示:classSolution{public: string qq[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};voiddfs(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 
代码展示:classSolution{public:/** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param numbers int整型vector * @return int整型 */intMoreThanHalfNum_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) 
代码展示:classSolution{public:intsingleNumber(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;(^=之间不能有空格) 
代码展示:classSolution{public: vector<int>singleNumber(vector<int>& nums){longlongint sum =0;for(auto e: nums){ sum = sum^e;}longlongint lowbit = sum&(-sum);int num1 =0,num2 =0;for(auto e:nums){if((lowbit&e)==0) num1^= e;else num2^=e;}return{num1,num2};}};

Read more

解锁DeepSeek潜能:Docker+Ollama打造本地大模型部署新范式

解锁DeepSeek潜能:Docker+Ollama打造本地大模型部署新范式

🐇明明跟你说过:个人主页 🏅个人专栏:《深度探秘:AI界的007》 🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、什么是Docker 2、什么是Ollama 二、准备工作 1、操作系统 2、镜像准备 三、安装 1、安装Docker 2、启动Ollama 3、拉取Deepseek大模型 4、启动Deepseek  一、引言 1、什么是Docker Docker:就像一个“打包好的App” 想象一下,你写了一个很棒的程序,在自己的电脑上运行得很好。但当你把它发给别人,可能会遇到各种问题: * “这个软件需要 Python 3.8,但我只有 Python 3.6!

By Ne0inhk
深挖 DeepSeek 隐藏玩法·智能炼金术2.0版本

深挖 DeepSeek 隐藏玩法·智能炼金术2.0版本

前引:屏幕前的你还在AI智能搜索框这样搜索吗?“这道题怎么写”“苹果为什么红”“怎么不被发现翘课” ,。看到此篇文章的小伙伴们!请准备好你的思维魔杖,开启【霍格沃茨模式】,看我如何更新秘密的【知识炼金术】,我们一起来解锁更加刺激的剧情!友情提醒:《《《前方高能》》》 目录 在哪使用DeepSeek 如何对提需求  隐藏玩法总结 几个高阶提示词 职场打工人 自媒体创作 电商实战 程序员开挂 非适用场地 “服务器繁忙”如何解决 (1)硅基流动平台 (2)Chatbox + API集成方案 (3)各大云平台 搭建个人知识库 前置准备 下载安装AnythingLLM 选择DeepSeek作为AI提供商 创作工作区 导入文档 编辑  编辑 小编寄语 ——————————————————————————————————————————— 在哪使用DeepSeek 我们解锁剧情前,肯定要知道在哪用DeepSeek!咯,为了照顾一些萌新朋友,它的下载方式我放在下面了,拿走不谢!  (1)

By Ne0inhk
【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

目录 一、前言 二、AI视频概述 2.1 什么是AI视频 2.2 AI视频核心特点 2.3 AI视频应用场景 三、通义万相介绍 3.1 通义万相概述 3.1.1 什么是通义万相 3.2 通义万相核心特点 3.3 通义万相技术特点 3.4 通义万相应用场景 四、DeepSeek + 通义万相制作AI视频流程 4.1 DeepSeek + 通义万相制作视频优势 4.1.1 DeepSeek 优势 4.1.2 通义万相视频生成优势 4.2

By Ne0inhk
【DeepSeek微调实践】DeepSeek-R1大模型基于MS-Swift框架部署/推理/微调实践大全

【DeepSeek微调实践】DeepSeek-R1大模型基于MS-Swift框架部署/推理/微调实践大全

系列篇章💥 No.文章01【DeepSeek应用实践】DeepSeek接入Word、WPS方法详解:无需代码,轻松实现智能办公助手功能02【DeepSeek应用实践】通义灵码 + DeepSeek:AI 编程助手的实战指南03【DeepSeek应用实践】Cline集成DeepSeek:开源AI编程助手,终端与Web开发的超强助力04【DeepSeek开发入门】DeepSeek API 开发初体验05【DeepSeek开发入门】DeepSeek API高级开发指南(推理与多轮对话机器人实践)06【DeepSeek开发入门】Function Calling 函数功能应用实战指南07【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:本地部署与API服务快速上手08【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:Web聊天机器人部署指南09【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:基于vLLM 搭建高性能推理服务器10【DeepSeek部署实战】基于Ollama快速部署Dee

By Ne0inhk