Vector

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析2

在这里插入图片描述


目录

  • 👉🏻vector概念
  • 👉🏻vector constructor
  • 👉🏻vector 容量
  • 👉🏻vector 增删查改
  • 🌅vector的模拟实现
    • 构造函数和析构函数
    • 拷贝构造、赋值、swap
    • size和capacity
    • reserve
    • resize
    • push_back
    • 迭代器和访问
    • insert
    • erase
    • 模板构造函数(迭代器区间)
    • 构造函数(初始化n个val值)
  • 🌩迭代器失效问题
    • insert和erase导致的迭代器失效
  • 👉🏻电话号码的字母组合

👉🏻vector概念

作为C++STL中的一个类模板,vector是一个动态数组容器,可以存储任意类型的元素。它提供了许多方便的方法来操作和管理数组。

以下是vector的一些特点和用法:

  1. 动态大小:vector可以根据需要自动调整大小,无需手动指定数组的大小。
  2. 随机访问:可以通过索引来访问和修改vector中的元素,例如myVector[0]。
  3. 插入和删除:可以在任意位置插入和删除元素,例如myVector.insert(position, value)和myVector.erase(position)。
  4. 动态增长:当vector的容量不足时,会自动分配更多的内存空间,以容纳更多的元素。
  5. 迭代器支持:可以使用迭代器来遍历vector中的元素,例如使用for循环或std::for_each函数。
  6. 元素访问:可以使用at()方法或[]运算符来访问元素,例如myVector.at(index)或myVector[index]。
  7. 大小和容量:可以使用size()方法获取vector中元素的数量,使用capacity()方法获取vector的容量。

vector官方文档

👉🏻vector constructor

在这里插入图片描述

  • vector()(重点) 无参构造
  • vector(size_type n, const value_type& val = value_type()) 构造并初始化n个val
  • vector (const vector& x); (重点) 拷贝构造
  • vector (InputIterator first, InputIterator last); 使用迭代器进行初始化构造

👉🏻vector 容量

  • size 获取数据个数
  • capacity 获取容量大小
  • empty 判断是否为空
  • resize(重点) 改变vector的size
  • reserve (重点) 改变vector的capacity

capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义
的。vs是PJ版本STL,g++是SGI版本STL。
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问

👉🏻vector 增删查改

  • push_back(重点) 尾插

  • pop_back (重点) 尾删

  • find 查找。(注意这个是算法模块实现,不是vector的成员接口)
    在这里插入图片描述

  • insert 在position之前插入val
    在这里插入图片描述

  • erase 删除position位置的数据

  • swap 交换两个vector的数据空间
    在这里插入图片描述

  • operator[] (重点) 像数组一样访问

🌅vector的模拟实现

构造函数和析构函数

 vector()
			 :_start(nullptr)
			 , _finish(nullptr)
			 , _endofstorage(nullptr)
			 {

			 }

		 ~vector()
			 {
				 delete[] _start;
				 _start = _finish = _endofstorage = nullptr;
		     }

拷贝构造、赋值、swap

 vector(const vector<T>& v)
			 :_start(nullptr)
			 , _finish(nullptr)
			 , _endofstorage(nullptr)
		 {
			 reserve(v.capacity());//先开一个和v一样大的空间
		     //而后直接将v中的数据插入到_start中
		 for (auto x : v)
		   {
			 push_back(x);

			 }
		 }
 //写完拷贝构造后,我们就可以开始写赋值了
			 vector<T>& operator=(vector<T> tmp)
		 {
			 swap(tmp);
			 return *this;
		 }
		void  swap(vector<T>& v)//打工人swap
		 {
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);

		 }

size和capacity

size_t size()
			{
				return _finish - _start;
			}
size_t capacity()
			{
				return _endofstorage - _start;
			}

reserve

void reserve(size_t n)
			{
				if (n > capacity())//不缩容
				{
					T* tmp = new T[n];
					size_t sz = size();//记录一下_finish和_start的偏移量✨
					if (_start != nullptr)
					{
						memcpy(tmp, _start, sizeof(T) * sz);
						delete[] _start;//释放旧空间

					}
					_start = tmp;
					_finish = tmp + sz;
					_endofstorage = _start + n;

				}
			}

这里memcpy()有个大坑,如果针对整型等浅拷贝的拷贝是没问题的。
但是如果拷贝的类型是string呢?
我们这里举个例子:

	void test_vector3()
	{
		vector<string> v;
		for (int i = 0; i < 5; i++)
		{
			v.push_back("11111111111111111111");
		}
		for (auto str : v)
		{
			cout << str << " ";
		}
	}

在这里插入图片描述
那么这里错在哪呢?
在这里插入图片描述
所以为了避免浅拷贝,我们还是只能自己动手,丰衣足食了,就不借助memcpy了。
这里我们直接用string的赋值运算符,可以直接拷贝构造无需担心浅拷贝问题。

void reserve(size_t n)
			{
				if (n > capacity())//不缩容
				{
					T* tmp = new T[n];
					size_t sz = size();
					if (_start != nullptr)
					{
						for (int i = 0; i < sz; i++)
						{
							tmp[i] = _start[i];
						}
						delete[] _start;//释放旧空间

					}
					_start = tmp;
					_finish = tmp + sz;
					_endofstorage = _start + n;

				}
			}

这样就解决问题了😄

resize

void resize(size_t n, const T& val = T())//因为T()是匿名对象是临时变量具有常性,所以引用传参不能权限放大得加const
			{
				if (n <= size())
				{
					_finish = _start + n;
				}
				else
				{
					reserve(n);//先开辟n大的空间
					//然后再进行插入数据
					while (_finish < _start + n)
					{
						*_finish = val;
						_finish++;
					}
				}
			}

push_back

void push_back(const T& x)
			{
				if (_endofstorage == _finish)
				{
					size_t cp = capacity() == 0 ? 4 : capacity() * 2;
					reserve(cp);
					//T* tmp = new T[cp];
					//if (_start != nullptr)
					//{
					//	memcpy(tmp, _start, sizeof(T) * size());
					//		delete[] _start;//释放旧空间
					//}

					//_finish = tmp + size();//_finish = tmp + size()一定要先写,那不然_start先修改,size()大小会不理想
					//_start = tmp;

					_endofstorage = _start + cp;
					
				}
				*_finish = x;
				_finish++;

			}

迭代器和访问

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;
		}
T& operator[](size_t pos)
			{
				assert(pos < size());
				return _start[pos];
			}
			T& operator[](size_t pos)const //写个常量访问
			{
				assert(pos < size());
				return _start[pos];
			}

insert

void insert(iterator pos, const T& x)
			{
				assert(pos >= _start);
				assert(pos <= _finish);
				if (_finish == _endofstorage)//扩容情况
				{
					//迭代器失效问题:扩容后_start指向新空间,但是Pos仍然指向旧空间
					//我们先记录原来_start和Pos的偏移量
					//扩容后,pos再指向新空间中新位置
					size_t offset = pos - _start;//记录偏移量
					reserve(capacity() == 0 ? 0 : capacity() * 2);
					pos = _start + offset;

				}
				iterator end = _finish - 1;
				while (end >= pos)
				{
					*(end + 1) = *end;//数据挪动
					end--;
				}
				*(pos) = x;
				++_finish;
			}

erase

会导致迭代器失效版本

void erase(iterator  pos)
			{
				assert(pos >= _start);
				assert(pos <= _finish);
				iterator end = _finish - 1;
				while (pos < end)
				{
				  *pos = *(pos + 1);
					pos++;
				}
				_finish--;

			}

模板构造函数(迭代器区间)

vector的构造函数中,还提供了模板构造函数。
在这里插入图片描述

 template <class InputIterator>
		 vector(InputIterator first, InputIterator last)
		  :_start(nullptr)
			 , _finish(nullptr)
			 , _endofstorage(nullptr)
		 {
			 while (first != last)
			 {
				 push_back(*first);
				 ++first;
			 }
		 }

有人会问,既然是用迭代器区间初始化,为什么不能用vector自己本身的iterator呢?
可以是可以,但格局就小啦。
在这里插入图片描述
如果我是用vector自己本身的iterator区间,那么假如说我这个vector的iterator是int*,那初始化也就是整型。
那如果我想初始化为字符串呢?
所以这里模板构造函数就显示出它的神通广大了,只要是迭代器区间,我都可以接收,这不香吗?😍

⭐️补充小知识点

当我们写了拷贝构造函数、模板构造函数、或者其它重载构造函数时
总之只要写了,默认无参构造函数一定要写出来(即使你并没有做什么)
默认无参构造函数是最基本的,如果不写,编译器就会使用它自己的默认构造函数。而这时,你写的其它构造函数都不会生效了

构造函数(初始化n个val值)

 vector(size_t n, const T val = T())
		 {
			 reserve(n);
			 for (size_t i = 0; i < n; i++)
			 {
				 push_back(val);
			 }
		 }
 vector(int n, const T val = T())
		 {
			 reserve(n);
			 for (int i = 0; i < n; i++)
			 {
				 push_back(val);
			 }
		 }

为什么这里还要多此一举再写个重载?
因为我们上面的模板函数缘故,为了使(int,int)的传参更好的匹配到构造函数(初始化n个val值),我们写了一个参数类型为int 的n

🌩迭代器失效问题

C++中的迭代器失效是指在对容器进行修改操作后,之前获取的迭代器可能会变得无效。这意味着在迭代器失效后,尝试使用该迭代器进行访问或操作容器的行为将导致未定义的行为。

迭代器失效的常见情况包括:

  • 在向容器中插入删除元素后,指向被修改位置的迭代器会失效。
  • 在对容器进行重分配内存的操作后,所有指向容器元素的迭代器都会失效。
  • 在使用迭代器遍历容器时,如果在遍历过程中对容器进行了修改操作,迭代器也会失效。

为了避免迭代器失效,可以采取以下措施:

  • 在进行插入或删除操作后,更新迭代器,使其指向有效的位置。
  • 在遍历容器时,避免在循环内部对容器进行修改操作。
  • 在进行可能导致迭代器失效的操作之前,先将迭代器保存下来,以便需要时重新定位

insert和erase导致的迭代器失效

在上述的vector模拟实现中,我们实现了insert和erase的功能。
但是它们会导致怎样的迭代器失效问题呢?
insert

	void insert(iterator pos, const T& x)
			{
				assert(pos >= _start);
				assert(pos <= _finish);
				if (_finish == _endofstorage)//扩容情况
				{
					//迭代器失效问题:扩容后_start指向新空间,但是Pos仍然指向旧空间
					//我们先记录原来_start和Pos的偏移量
					//扩容后,pos再指向新空间中新位置
					size_t offset = pos - _start;//记录偏移量
					reserve(capacity() == 0 ? 0 : capacity() * 2);
					pos = _start + offset;

				}
				iterator end = _finish - 1;
				while (end >= pos)
				{
					*(end + 1) = *end;//数据挪动
					end--;
				}
				*(pos) = x;
				++_finish;
			}

上述代码是经过优化改正的,这个是已经解决迭代器问题了。
但我们还得分析一下问题所在
为什么要记录pos和_start偏移量?
因为我们在扩容时,会开辟新空间,当_start指向新空间时,此时pos还是指向旧空间。
所以为了使pos指向正确的位置,我们得先记录它和_start的偏移量,而后新的_start加上偏移量就是pos在新空间的位置。

erase
会导致迭代器失效版本

void erase(iterator  pos)
			{
				assert(pos >= _start);
				assert(pos <= _finish);
				iterator end = _finish - 1;
				while (pos < end)
				{
				  *pos = *(pos + 1);
					pos++;
				}
				_finish--;

			}

我们举个删除偶数的例子。

void test_vector2()
	{
		vector<int> v;
		for (int i = 1; i <= 6; i++)
		{
			v.push_back(i);

		}
		
		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				v.erase(it);
			}
			it++;
		}
		for (auto x : v)
		{
			cout << x << " ";
		}
	}

在这里插入图片描述
我们发现这里崩溃出错了,而且问题是pos>_finish引起的。
我们看看具体过程
在这里插入图片描述
上述过程中的主要问题就是it进行++后指向出现的问题。
那么怎么解决这个问题呢?
实际上c++官方就已经注意到了这个问题,所以,它所设置的erase,会返回函数调用擦除的元素后面元素的新位置的迭代器。
在这里插入图片描述
所以,在模拟实现这里我们也要这么做。

iterator erase(iterator  pos)
			{
				assert(pos >= _start);
				assert(pos <= _finish);
				iterator end = _finish - 1;
				iterator p = pos;
				while (p < end)
				{
				  *p = *(p + 1);
					p++;
				}
				_finish--;
				return pos;//返回被删的位置
			}

所以可以有

while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it = v.erase(it);
			}
			else
			it++;//不是偶数再++
		}

总结
上述中,我们解决迭代器失效问题的主要思路就是在进行插入或删除操作后,更新迭代器,使其指向有效的位置
而迭代器所谓失效,就是看看在具体情况中,它所指向的元素位置是否和我们预想的有所偏差,如果有偏差,则就会导致迭代器失效

👉🏻电话号码的字母组合

原题链接:电话号码的字母组合

class Solution {
public:
   const char* numsArr[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
   void Combine(const string& digits,string combinestr,int i ,vector<string>& ret)
   {
       if(i == digits.size())//当数字中的字母全部都进行完组合后
       {
           ret.push_back(combinestr);
           return;
       }
      int num = digits[i] - '0';
      string str = numsArr[num];
       for(auto ch:str)//将当前数字代表的全部字母拿去依次拿去跟其它数字的字母进行组合
       {
           Combine(digits,combinestr+ch,i+1,ret);
       }
   }
    vector<string> letterCombinations(const string& digits) {
           vector<string> v;//存储全部组合的字符串
           if(digits=="")
           return v;
           string str;//这个是专门用来组合的字符串
           int i =0;
           Combine(digits,str,i,v);
           return v;
 
    }
};

如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/78014.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python爬虫——scrapy_工作原理

引擎向spiders要url引擎把将要爬取的url给调度器调度器会将url生成的请求对象放入到指定的队列中从队列中出队一个请求引擎将请求交给下载器进行处理下载器发送请求获取互联网数据下载器将数据返回给引擎引擎将数据再次给到spidersspiders通过xpath解析该数据&#xff0c;得到数…

召集令:CloudQuery 社区有奖征文活动来啦!

CloudQuery 社区第一期征文活动来袭&#xff01;&#xff01;&#xff01;只要你对 CloudQuery 产品感兴趣&#xff0c;或者是希望了解 CQ &#xff0c;都可以来参加&#xff0c;在本期活动中&#xff0c;我们也为大家准备了多种主题供你选择&#xff0c;CQ 使用案例、版本对比…

字符设备驱动分布注册

驱动文件&#xff1a; 脑图&#xff1a; 现象&#xff1a;

Open3D 进阶(5)变分贝叶斯高斯混合点云聚类

目录 一、算法原理二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 系列文章(连载中。。。爬虫,你倒是爬个完整的呀?): Open3D 进阶(1) MeanShift点云聚类Open3D 进阶(2)DB…

Windows11 Docker Desktop 启动 -wsl kernel version too low

系统环境&#xff1a;windows11 1&#xff1a;docker下载 Docker: Accelerated Container Application Development 下载后双击安装即可 安装后启动Docker提示&#xff1a;Docker Desktop -wsl kernel version too low 处理起来也是非常方便 1:管理员身份启动&#xff1a;…

用于弥散加权MRI的关节各向异性维纳滤光片研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

第十三课:QtCmd 命令行终端应用程序开发

功能描述&#xff1a;开发一个类似于 Windows 命令行提示符或 Linux 命令行终端的应用程序 一、最终演示效果 QtCmd 不是因为它是 Qt 的组件&#xff0c;而是采用 Qt 开发了一个类似 Windows 命令提示符或者 Linux 命令行终端的应用程序&#xff0c;故取名为 QtCmd。 上述演示…

SSD202D-logo分区添加dtb

SSD202D-kernel-uimage后面加入dtb_旋风旋风的博客-CSDN博客 1.由于内核的uimage老是压缩解压缩,拿到压缩包里面dtb实在困难; 2.把dtb烧在后面又有安全隐患;而且还会有打包升级方法ota之类的很多;又毙掉了, 3.最后直接把dtb放在logo的包里,但是logo包要想添加好,也要深刻的理…

谷歌云 | BigQuery 现在支持用于查询开放表格式的清单文件

Cloud Ace 是谷歌云全球战略合作伙伴&#xff0c;拥有 300 多名工程师&#xff0c;也是谷歌最高级别合作伙伴&#xff0c;多次获得 Google Cloud 合作伙伴奖。作为谷歌托管服务商&#xff0c;我们提供谷歌云、谷歌地图、谷歌办公套件、谷歌云认证培训服务。 开放表格式依赖嵌…

Golang下载安装

目录 1. 下载压缩包 2. 解压 3. 查看SDK是否安装成功 4. 配置环境变量 5. 查看环境变量是否配置成功 1. 下载压缩包 官网下载地址&#xff1a; All releases - The Go Programming Language Windows64位选择如下下载&#xff1a; 2. 解压 解压后内容如下&#xff1a; …

开源数据库Mysql_DBA运维实战 (DDL语句)

DDL DDL语句 数据库定义语言&#xff1a;数据库、表、视图、索引、存储过程. 例如:CREATE DROP ALTER DDL库 定义库{ 创建业务数据库&#xff1a;CREAATE DATABASE ___数据库名___ ; 数据库名要求{ a.区分大小写 b.唯一性 c.不能使用关键字如 create select d.不能单独使用…

unity拓展 unity自带的类(Tranform为例)

因为我们使用了ILRuntime热更&#xff0c;unity 打出的WebGL包&#xff0c;运行就会报找不到DoTween里面的方法&#xff0c;所以吧DoTween拓展到tranform类里面&#xff0c;这样就不会报错了&#xff0c;下面是示例 using DG.Tweening; using System.Collections; using Syste…

【CSS动画01--登录】

CSS动画01--登录 介绍代码HTMLCSSJS 介绍 当鼠标不同方向的划过时展示不同效果的登录&#xff0c;以上是一个简单的图片展示 代码 HTML <!DOCTYPE html> <html> <head><meta http-equiv"content-type" content"text/html; charsetutf-8&…

【第二讲---初识SLAM】

SLAM简介 视觉SLAM&#xff0c;主要指的是利用相机完成建图和定位问题。如果传感器是激光&#xff0c;那么就称为激光SLAM。 定位&#xff08;明白自身状态&#xff08;即位置&#xff09;&#xff09;建图&#xff08;了解外在环境&#xff09;。 视觉SLAM中使用的相机与常见…

Linux命令200例:crontab详解及应用场景(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

编译老版本c++程序 报错 msvcrt.dll 以及 0x000000 内存 不能为 “read“ 问题已解决

一般 win10 编译 xp对应老版本软件 调试采用 虚拟机形式进行测试&#xff0c;但是虚拟机中&#xff0c;无独立显卡&#xff0c;运行程序提示有&#xff0c;无法调用动态库&#xff0c;或者 内存无法读取&#xff0c;炸一看以为 winxp32位 内存识别只能3.7G.其实是显存无法使用…

idea 使用debug 启动项目的时候 出现 Method breakpoints may dramatically slow down debugging

问题: 1. 写了一段时间的代码&#xff0c;在debug启动项目后提示&#xff1a;Method breakpoints may dramatically slow down debugging 但是正常启动是可以的&#xff0c;debug不行。 2. idea 里面的项目&#xff0c;很多地方都有断点&#xff0c;现在想要取消全部的断点…

重塑DTC规则:元气森林的全渠道转型

元气森林作为迄今为止用5-6年时间最快达到70亿年销售额的饮料品牌&#xff08;统一、可口可乐、东鹏特饮都花了15年左右&#xff0c;康师傅花了10年&#xff09;。元气森林于2016年在北京创立&#xff0c;凭借健康产品理念和新潮营销方式&#xff0c;一款主打“0糖0卡0脂”概念…

k8s 认证和权限控制

k8s 的认证机制是啥&#xff1f; 说到 k8s 的认证机制&#xff0c;其实之前咋那么也有提到过 ServiceAccouont &#xff0c;以及相应的 token &#xff0c;证书 crt&#xff0c;和基于 HTTP 的认证等等 k8s 会使用如上几种方式来获取客户端身份信息&#xff0c;不限于上面几种…

JDBC封装与设计模式

什么是 DAO &#xff1f; Data Access Object(数据存取对象) 位于业务逻辑和持久化数据之间实现对持久化数据的访问 DAO起着转换器的作用&#xff0c;将数据在实体类和数据库记录之间进行转换。 ----------------------------------------------------- DAO模式的组成部分 …
最新文章