Cpp || vector学习

本文详细解析了C++ STL中的vector容器,包括其内存管理策略、迭代器使用、增删改查操作及注意事项。探讨了vector在不同场景下的应用,如数据查找、空间增长策略、迭代器失效处理等,提供了丰富的代码示例。

vector 的相关知识

1.vector的概述
  1. vector是表示可变大小数组的序列容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在
    末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。

vector容器类型,而vector<数据类型> 定义的s是对象类型

vector< int > a;
a为定义的vector< int >类型的对象

string中的数据是以’\0’结束的,而vector中的数据不是以’\0’结束,其按指针来标记

vector中可以管理不同类型的数据

用vector实例化对象
vector<int > v; //vector<int>才是对象v的类型
用vector实例化对象并进行初始化的方式
vector<char> v2(10,'x');
vector<int> v4(10, 2);
//两种方式都是将实例化对象的前10个元素初始化为指定的值
  • 或者是这种形式的使用
string s("hello bit!");
vector<char> v3(s.begin(),s.end());

利用迭代器的方式来初始化实例化的对象v3;

2.关于vector类型迭代器的使用和遍历
2.1:vector iterator 的使用
iterator的使用接口说明
begin()获取第一个数据位置的iterator
end()获取最后一个数据的下一个位置的iterator
rbegin()获取最后一个数据位置的reverse_iterator
rend()获取第一个数据前一个位置的reverse_iterator
cbegin()获取第一个数据位置的const_iterator
cend()获取最后一个数据的下一个位置的const_iterator

在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<vector>

using namespace std;

int main(){
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
    //以尾插的方式将内容添加到容器中

	for (int i = 0; i < v.size(); i++){
		cout << v[i] << " ";
	}
	cout << endl;

	//普通迭代器方式访问
	//vector<int>::iterator vit = v.begin();
	auto vit = v.begin();
    //使用auto关键字后其自动匹配迭代器类型
	while (vit!=v.end()){
		cout << *vit << " ";
        //需要输出的是迭代器中的内容
		vit++;
	}
	cout << endl;
	//反向迭代器的访问方式
	auto vit1 = v.rbegin();
	while (vit1 != v.rend()){
		cout << *vit1 << " ";
		vit1++;
	}
	cout << endl;
//遍历迭代器中内容的时候不能改变其内容const 迭代器的使用
	vector<int>::const_iterator  cvit = v.begin();
	while (cvit != v.end()){
		cout << "const:" << *cvit<<" ";
		cvit++;
	}
	cout << endl;
	//基于范围的for循环,内部调用迭代器的begin和end;
	for (auto &e : v){
		cout << e << " ";
		//e++;//此处的e匹配的是迭代器中的元素
		//若先++后输出的话就改变了迭代器本来要输出的内容
	}
	cout << endl;
	return 0;
}

该程序中利用不同的方式遍历了整个容器,在定义迭代器的时候除了使用auto关键字自动匹配外,还在使用vector定义迭代器。但是定义时要注明类型:其使用方式为 vector<>的形式

#include<iostream>
#include<vector>

using namespace std;
void printVector(vector<int> &v){
	//对于只是简单打印的可以传引用,进而不进行值拷贝
	//若希望不能修改迭代器中的内容,传参数的时候可以传const修饰的参数
	//形如:const vector<int> &v;
	auto vit1 = v.begin();
	while (vit1 != v.end()){
		cout << *vit1 << " ";
		vit1++;
	}
	cout << endl;
}

int main(){
	vector<int> v(10, 1);
	printVector(v);
	//max_size();显示迭代器中可以存放的最多元素数量
	vector<int> v1;
	vector<char> v2;
	vector<double> v3;
	cout << v1.max_size() << endl;  //1G
    //查看指定对象的最大容量
	cout << v2.max_size() << endl;  //4G
	cout << v3.max_size() << endl;  //0.5G
	return 0;
}

2.1:vector 空间增长问题
容量空间接口说明
size()获取数据个数
capacity()获取容量大小
empty()判断是否为空
void resize (size_type n, value_type val = value_type());改变vector的size
void reserve (size_type n);改变vector放入capacity
  • capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。

  • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问
    题。

  • resize在开空间的同时还会进行初始化,影响size。

#include<iostream>
#include<vector>

using namespace std;


int main(){
  //vector::capacity()的使用
  vector<int> v(10,5);
  cout<<v.capacity()<<endl;
  
  //vector::reserve()的使用
  v.reserve(20);
  cout<<v.capacity()<<endl;
  
  vector<int>::iterator vt=v.begin();
  while(vt!=v.end()){
    cout<<*vt<<" ";
    vt++;
  }
  cout<<endl;

  v.resize(30,1);
  vector<int>::iterator vt1=v.begin();
  while(vt1!=v.end()){
    cout<<*vt1<<" ";
    vt1++;
  }
  cout<<endl;
  return 0;
}
  • 运行结果

在这里插入图片描述

3.vector 增删改查
vector增删查改接口说明
void push_back (const value_type& val);尾插
void pop_back();尾删
InputIterator find (InputIterator first, InputIterator last, const T& val);查找。(注意这个是算法模块实现,不是vector的成员接口
iterator insert (iterator position, const value_type& val);在position之前插入val
iterator erase (iterator position);删除position位置的数据
void swap (vector& x);交换两个vector的数据空间
reference operator[] (size_type n);像数组一样访问
3.1.push_back() 和 pop_back() 接口的使用
  • push_back和pop_back()
#include<iostream>
#include<vector>

using namespace std;

void printVector(vector<int> &v){
    //打印函数
	auto vit1 = v.begin();
	while (vit1 != v.end()){
		cout << *vit1 << " ";
		vit1++;
	}
	cout << endl;
}

//insert接口的测试
void testVector(){
	vector<int> v;
	//vector容量初始值为0(不存放内容的时候)
	//之后扩容的时候window按照原来容量的1.5倍进行扩容
	//Linux按照原来的2倍进行扩容
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.pop_back();
	
	v.insert(v.begin(), 0);  //头插
	v.insert(v.end(), 6);   //尾插
	//函数的参数不再是位置,而是迭代器
	printVector(v);
	v.insert(++v.begin(),10,20);
	//迭代器可以++;首元素的下一个元素
	printVector(v);
}

//扩容的是否会导致迭代器失效
//相当于一个野指针
int main(){
	testVector();
	return 0;
}

3.2.find/insert/erase接口的使用
  • find/insert/erase
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void printVector(vector<int> &v){
    auto vit1 = v.begin();
    while (vit1 != v.end()){
          cout << *vit1 << " ";
              vit1++;
                
    }
      cout << endl;

}

void Print(vector<int> &v){
  auto vit=v.begin();
  while(vit!=v.end()){
    cout<<*vit<<" ";
    vit++;
  }
  cout<<endl;
}
void Testfind() {

  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  v.push_back(5);
  Print(v);
  vector<int>::iterator pos=find(v.begin(),v.end(),9);
  //没有找到的时候,返回的是v.end()的位置
  v.insert(pos,6);
  //指定位置插入内容,将后面的内容往后弄
  Print(v);
}

//erase删除位置的迭代器

void testErase(){
  vector<int> v(10, 2);
  v.erase(v.begin());
  printVector(v);
  v.erase(v.begin(), v.end()-3);
  //迭代器可以进行加减运算
  printVector(v);
}

int main(){
  testErase();
  Testfind();
  return 0;
}

若删除的是一个元素(参数是删除位置的迭代器)

iterator erase(iterator pos);

删除的是连续几个元素的话(参数为两个迭代器)

 iterator erase(iterator first,iterator last);

从迭代器first开始删除到迭代器last;

4.迭代器失效

迭代器失效:迭代器指向的位置无效/越界

4.1.insert(iterator it,T x);

插入可能导致增容,会释放原有的空间,开辟新的空间,拷贝原有的内容
插入之前的迭代器位置可能已经被释放,变成类似为野指针的迭代器
调用完insert接口,重新获取迭代器,防止迭代器失效

4.2.调用erase(iterator it);

访问越界,调用完erase接口,获取erase接口的返回值,防止迭代器失效

#include<iostream>
#include<vector>
using  namespace std;
void printVector(vector<int> &v){
  //打印函数
	auto vit1 = v.begin();
	while (vit1 != v.end()){
		cout << *vit1 << " ";
		vit1++;
	}
	cout << endl;
}

void testErase(){
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	auto vit = v.begin();
	while (vit != v.end()){
		if (*vit % 2 == 0)
			vit=v.erase(vit);
//调用完erase接口,获取erase接口的返回值,防止迭代器失效
//若不用vit去接收这个接口返回值的话,释放一次后程序奔溃
//这样赋值的目的是防止迭代器指向的位置是一个无效的位置。
//拿取erase接口的返回值(返回值为迭代器)
		else
			vit++;
	}
	printVector(v);
}
int main(){
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	testErase();
	//auto vit = v1.begin();
	//v1.insert(vit,10);  
	//在插入的时候进行了扩容操作,该过程中重新开辟了空间
	//导致原来的空间失效(迭代器失效),

	auto vit = v1.begin();
	v1.insert(vit, 10);
	vit = v1.begin();
	//调用完insert接口,重新获取迭代器,防止迭代器失效
	*vit = 20; //本质:野指针(指向已经释放的空间)
	v1.push_back(3);
	v1.push_back(4);
	printVector(v1);
	return 0;
}
5.swap函数的使用
全局函数swap和成员函数swap两个函数本质上没有区别
string类中的swap函数

全局函数:swap(s1,s2)–>{s1.swap(s2)}

本质上内部调用的为string的成员函数

成员函数:swap(s2)–>s1.swap(s2);
全局的swap只是对成员的swap函数进行了封装
通用全局变量:swap(T& s1,T& s2)–>{T temp=s1;s1=s2;s2=temp;} 内部进行了拷贝

vector的swap函数

全局函数:swap(s1,s2)–>{s1.swap(s2)}//本质上内部调用的为vector的成员函数
成员函数:swap(s2)–>s1.swap(s2);
全局的swap只是对成员的swap函数进行了封装

#include<iostream>
#include<veector>
using namespace std;

void printVector(const vector<int> &v){
	for (auto& e : v){
		cout << e << " ";
	}
	cout << endl;
}

int main(){
	vector<int> v1(3, 2);
	vector<int> v2(3, 4);
	printVector(v1);
	printVector(v2);
	//全局的交换函数
	swap(v1, v2);
	cout << "全局函数交换后:" << endl;
	printVector(v1);
	printVector(v2);
	//成员函数
	v1.swap(v2);
	cout << "成员函数交换后:" << endl;
	printVector(v1);
	printVector(v2);
	return 0;
}
6.手动释放vector的空间
1.在一个局部域中定义一个空的vector
2.调用vector swap函数,完成空间的交换
3.出了局部域,编译器自动调用局部域中定义的局部变量的析构函数,完成空间的释放{vector < T > tmp; tmp.swap(v);}
#include<iostream>
#include<vector>
using namespace std;
int main(){
	vector<int> v(90, 1);
	cout << v.capacity() << endl;
	//No1:
	{
		vector<int> v1;
		//定义的时候没有初始化,其内部没有内容容量为0
		v1.swap(v);
	}//加了{}不会将v原来的空间释放掉
	//(加了{}会在出作用域的时候调用析构函数释放局部变量v1)
	cout << "No1:"<<v.capacity() << endl;
	//No2:

	vector<int> v1(90, 1);
	cout << v1.capacity() << endl;
	v1.resize(0);//改变v1的有效字符数
	v1.shrink_to_fit(); //释放空间
	cout << "No2:" << v1.capacity() << endl;
	return 0;
}
OJ中的使用

题目要求:查找一个数组中没有重复项的那个数

class Solution {
public:
 int singleNumber(vector<int>& nums) {
 int value = 0;
 for(size_t i =0 ; i < nums.size(); ++i){
 value ^= nums[i];
 }
 return value;
 }
};
//在c++11中

class Solution {
public:
 int singleNumber(vector<int>& nums) {
 int value = 0;
 for(const auto& e:nums){
 value ^= e;
 }
 return value;
 }
};
//迭代器值的交换接口
v1.swap(v2);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值