C++ STL--vector详解

1.Vector简介

(1)vector是表示可变大小数组的序列容器,也就是一个变长数组,是C语言数组的plus加强版。

(2)就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。

(3)本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因此每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小,其分配空间策略见第四点。

(4)vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是 对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。类似于数据结构中当存储结构内存不足时申请空间的操作。

(5)与其他容器相比,vector访问元素更高效,因为和数组一样可以直接用下标访问。

2.vector的使用

看文档是一种很重要的学习方法,以下是vector的文档链接:

http://www.cplusplus.com/reference/vector/vector/

2.1 vector的定义

以上是vector的几种定义方式, 

2.2 vector iterator的使用

iterator是一种设计模式,用于遍历集合中的元素。vector iterator是指针的一种类型,用于在vector容器中迭代(遍历)元素,类似于使用指针访问数组的方式。

(1)begin() 和 end()  重点掌握

begin()用于获取第一个数据位置的iterator/const_iterator,end()用于获取最后一个数据的下一个位置的iterator/const_iterator

(2)rbegin()和rend()

也就是begin和end的倒置,rbegin()获取最后一个数据位置的reverse_iterator,rend()获取第一个数据前一个位置的 reverse_iterator

注意:这里的rbegin()在遍历时仍旧使用++的方式,后面会代码实现。

2.3  vector的空间增长问题

在简介中提到了vector是一个动态数组容器,既然是动态的,那么空间问题就是一个不可避免的问题。

size()用于获取数据个数,类似于C语言中的sizeof(arr)/sizeof(arr[0])的操作。

capacity()用于获取容量大小,但注意容量和数据个数的区别。

resize可以改变vector的size,reserve可以改变vector的capacity,如果是想要预留空间的话使用reverse,resize会直接改变元素个数,增加的部分会初始化为0

empty用于判断vector是否为空,指的是size是否为0,为空返回true,非空返回false

下图是代码实现:

2.4 vector的初始化和遍历

vector的几种构造函数,对应以下几种定义方式:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

void test_vector1()
{
	vector<int> v1;//无参的构造
	vector<int> v2(10, 0);//初始化v2,v2内有10个0
	vector<int> v3(v2.begin(), v2.end());//通过迭代器来初始化,将v2的内容全部拷贝给v3
	//也可以通过迭代器将其他容器的内容拷贝到vector中
	string s("JINITAIMEI");
	vector<int> v4(s.begin(), s.end());//发生了隐式类型转化,将char类型转化为int类型。
	vector<int> v5(v4);//拷贝构造
}

int main()
{
	test_vector1();
	return 0;
}

打印结果如下(全部使用范围for访问,范围for相关知识C++11中基于范围的for-CSDN博客): 

 对于vector的遍历,在C++11后使用范围for是很方便的。如果不使用范围for,一般会使用下标遍历或者迭代器遍历的方法,同时vector也是支持下标访问的,也就是[ ],这是运算符重载。

在使用迭代器时,注意begin指向的是vector首个元素的位置,而end指向的是最后一个元素的后一个位置,所以while循环判定继续的标志应该是!=v.end();

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

void test_vector1()
{
	vector<int> v1;//无参的构造
	vector<int> v2(10, 0);//初始化v2,v2内有10个0
	vector<int> v3(v2.begin(), v2.end());//通过迭代器来初始化,将v2的内容全部拷贝给v3
	//也可以通过迭代器将其他容器的内容拷贝到vector中
	string s("JINITAIMEI");
	vector<int> v4(s.begin(), s.end());//发生了隐式类型转化,将char类型转化为int类型。
	vector<int> v5(v4);//拷贝构造

	cout << "v2: ";
	for (size_t i = 0; i < v2.size(); i++)//使用成员函数size()获取vector内元素的数量然后遍历
	{
		cout << v2[i] << " ";
	}
	cout << endl;

	cout << "v3: ";
	//vector<int>::iterator it = v3.begin();//迭代器的类型是vector<int>::iterator,当然也可以用auto直接推出类型
	auto it = v3.begin();
	while(it!=v3.end())//使用迭代器遍历
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	cout << "v4: ";
	for (auto x : v4)
	{
		cout << x << " ";
	}
	cout << endl;
	cout << "v5: ";
	for (auto x : v5)
	{
		cout << x << " ";
	}
	cout << endl;
}

int main()
{
	test_vector1();
	return 0;
}

2.5 vector的增删查改

尾插、尾删 

就是直接调用成员函数,很常用且用法简单。


 find查找

注意find不是vector的成员接口,是算法库提供的通用的,使用时需要头文件#include<algorithm>,并且值得注意的是迭代器中传区间都是左闭右开。

 

如果找到了就返回迭代器(指针),如果没有找到,就返回最后一个元素的下一个地址,也就是v.end()


insert插入

上图是insert的调用,从上往下依次为:

(1)在pos位置前插入val----------- v . insert ( pos , val );

(2)在pos位置前插入n个val;----------- v. insert (pos , n , val );

(3)在pos位置前插入一段迭代器区间 ----------- v. insert ( pos , frist , last );

插入常常与find一起使用,用来获取pos的位置,下面是一个例子:

int main()
{
	vector<int> v;
	v.push_back(1);//尾插
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//通过迭代器确定查找范围,返回iterator指针
	vector<int>::iterator it = find(v.begin(), v.end(), 3);
	v.insert(it, 6);//在3前插入一个6
	for (auto x : v)
	{
		cout << x << " ";
	}
	cout << endl;
	it = find(v.begin(), v.end(), 3);//此时3的位置改变了,需要重新获取
	v.insert(it, 3, 6);//在3前插入3个6
	for (auto x : v)
	{
		cout << x << " ";
	}
	cout << endl;
	vector<int> v1 = v;
	it = find(v.begin(), v.end(), 3);
	v.insert(it, v1.begin(), v1.begin() + 2);//在3前插入一段迭代器区间
	for (auto x : v)
	{
		cout << x << " ";
	}
	cout << endl;
	return 0;
}


erase 删除

(1)删除pos位置的元素----------- v.erase( pos );

(2)删除一段迭代器空间------------v .erase( first , last );


swap 交换

在算法库里面也有一个通用的swap,但是通用swap会涉及深拷贝,会降低运行速度。 

3. vector的动态二维数组 

动态二维数组其实就是一个vector中的每一个元素都是一个vector<T>*,指向几个连续的,所以格式为

vector<vector<T>>  T为类型

3.1初始化

vector动态二维数组的存储是比较特殊的,下面的链接讲述了存储方式,但如果只考虑到使用的话,可以这样理解:vector动态二维数组可以同理为一个指针数组,这个指针数组的每个元素又指向一个个的数组,这些数组在物理上是不连续的,由此构成了动态二维数组。当然这是不严谨的。

C++ vector动态二维数组的存储问题-CSDN博客

这里使用了resize对有多少行进行了设定。这是一定要进行的操作,就相当于C语言二维数组需要定义有多少列类似,其实也就是对需要多少个指向vector的指针做了规划,如果没有强行插入就会越界。但不同于C语言二维数组的是,它在物理上不是连续的,所以是可以随时使用resize改变其行数,如下所示:

3.2增删查改:

这些操作都是先确定在哪一行,再进行和一维一样的操作即可。

尾插、尾删

动态二维数组的尾插是在确定的一行进行插入,例如v[0].push_back(1)是在第0行尾插了一个1,因为push_back会自动扩容,所以不需要确定有多少列。

同理pop_back尾删也需要先确定在哪一行进行尾删,例如v[1].pop_back()是在第一行进行了尾删


find查找、iterator、insert、erase、size

看图即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值