首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C++STL :vector类 (一) 】详解vector类的使用层&&vector实践:算法题练习

【C++STL :vector类 (一) 】详解vector类的使用层&&vector实践:算法题练习

作者头像
艾莉丝努力练剑
发布2025-11-17 09:14:23
发布2025-11-17 09:14:23
1740
举报
文章被收录于专栏:C / C++C / C++

C++的两个参考文档

老朋友(非官方文档):cplusplus 官方文档(同步更新):cppreference

vector容器文档链接:vector

1 ~> vector容器

1.1 vector的接口数比string类少

前面我们解释过,string类比STL诞生要早几年,STL很多容器和string都具有相似性,但是string的很多接口非常鸡肋,而且数量多——一百多个接口,博主也介绍过后面的STL借鉴了string,而且取长补短,接口数量大幅减少,就比如今天我们要正式开始介绍的一个新的容器——vector。

如下图所示,是不是少了很多?而且,有没有感觉这些接口很眼熟?没错,和string非常相似,在底层实现层面会有差异,但是使用层面是差不多的,类似于复用的思想,这也就是为什么我们前面要花那么多的篇幅来详细介绍string类——后面的vector、list...都是差不多的。

1.2 vector的介绍

我们早在介绍STL是什么、怎么学习的那篇博客里面就提到了使用STL的三个境界:能用、明理、能扩展 ,那么下面进入到vector的学习,我们也是按照这个方法。

cplusplus中的vector介绍

1.3 vector的理解

string是字符串,vector则是一个改变数据的顺序容器,其实对应的就是博主之前在用C语言实现初阶的数据结构里面实现过的顺序表。可以理解为C++版本的顺序表。

1.4 vector的定义

(constructor)构造函数声明

接口说明

(重点)vector( )

无参构造

vector(size_type n, const value_type& val = value_type())

构造并初始化n个val

(重点)vector (const vector& x);

拷贝构造

vector (InputIterator first, InputIterator last);

使用迭代器进行初始化构造


2 ~> vector的使用:结合文档

2.1 没有输入输出流,无法cout——自己Print

vector没法cin、cout,我们要想输出结果,就得自己封装一个Print函数——

代码语言:javascript
复制
void Print(const vector<int>& v)
{
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
}

写完这个,我们就可以偷个懒,用范围for——

代码语言:javascript
复制
	//范围for
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

2.2 迭代器:vector iterator 的使用

2.2.1 概念理解

iterator的使用

接口说明

(重点)begin+end

获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator

rbegin+rend

获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

2.2.2 实践

代码演示如下——

代码语言:javascript
复制
	vector<int>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << "it" << " ";
		++it;
	}
	cout << endl;

这个可以封装到Print函数里面——

调用前面已经封装好的Print函数,构造、输出——

2.3 vector的空间增长问题

2.3.1 文档接口理解

容量空间

接口说明

size

获取数据个数

capacity

获取容量大小

empty

判断是否为空

resize

改变vector的size

reserve

改变vector的capacity

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

2.3.2 不同环境下capacity增长倍数问题

我们来测试一下vector的默认扩容机制——

代码语言:javascript
复制
// 测试vector的默认扩容机制
void TestVectorExpand()
{
	size_t sz;
	vector<int> v;
	sz = v.capacity();
	cout << "making v grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

vs环境下的运行结果:vs下使用的STL基本是按照1.5倍方式扩容——

代码语言:javascript
复制
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 3
capacity changed: 4
capacity changed: 6
capacity changed: 9
capacity changed: 13
capacity changed: 19
capacity changed: 28
capacity changed: 42
capacity changed: 63
capacity changed: 94
capacity changed: 141

g++的运行结果:linux下使用的STL基本是按照2倍方式扩容——

代码语言:javascript
复制
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128

VS是按第一次2倍、后面1.5倍进行扩容的——

g++是按2倍进行扩容的——

2.3.3 reserve:提前将空间设置足够,提高效率

如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够,这样就可以避免边插入边扩容导致效率低下的问题了——主要是避免一边插入一边扩容——

代码语言:javascript
复制
// 如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够
// 就可以避免边插入边扩容导致效率低下的问题了
void TestVectorExpandOP()
{
	vector<int> v;
	size_t sz = v.capacity();
	v.reserve(100);   // 提前将容量设置好,可以避免一遍插入一遍扩容
	cout << "making bar grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}
2.3.4 实践

2.4 vector的增删查改

vector增删查改

接口说明

push_back

尾插

pop_back

尾删

find

查找(注意这个是算法模块实现,不是vector的成员接口)

insert

在position之前插入val

erase

删除position位置的数据

swap

交换两个vector的数据空间

operator[ ]

像数组一样访问

接口

功能说明

函数原型

注意事项

push_back

尾插

void push_back(const T& x);

会导致size加1; 可能引起重新分配,导致所有迭代器失效

pop_back

尾删

void pop_back();

会导致size减1; 空vector上调用是未定义行为

find

查找

InputIterator find(InputIterator first, InputIterator last, const T& val);

算法模块实现,非vector成员; 需要包含<algorithm>头文件

insert

在position之前插入val

iterator insert(iterator position, const T& x);

可能导致重新分配; 会使从插入点到末尾的迭代器失效

erase

删除position位置的数据

iterator erase(iterator position); iterator erase(iterator first, iterator last);

会使被删元素之后的迭代器失效; 返回指向下一个元素的迭代器

swap

交换两个vector的数据空间

void swap(vector& x);

高效操作,只交换内部指针; 迭代器在swap后仍然有效但指向另一容器

operator[]

像数组一样访问

reference operator[](size_type n); const_reference operator[](size_type n) const;

不进行边界检查; 下标越界是未定义行为

2.4.1 vector也只有尾插和尾删

和string一样,vector也只有尾插和尾删,没有头插和头删,因为要挪动数据,效率太低了——

我们要头插、或者头删和string一样,直接用insert和erase就行啦。

2.4.2 insert && erase实践

2.5 如果你觉得库里面的不好用可以用自己定义的模版

这个要当心,这个如果不用就注释掉,否则会跟库冲突——

2.6 简单了解一下emplace

2.6.1 简单了解

emplace可以简单理解为功能和insert差不多,而emplace_back和push_back功能一样,但前者效果更好,这个emplace我们在C++11会细讲。

2.6.2 对比

上图和下图的差异:传参,推荐用下面的写法——

2.6.3 实践

3 ~> vector实践:两道算法题

3.1 只出现一次的数字

力扣链接:136. 只出现一次的数字

题目描述:

3.1.1 代码实现

代码演示如下——

代码语言:javascript
复制
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int value = 0;
        for(auto e : nums)
        {
            //异或
            value ^= e;
            cout << endl;
        }
        return value;
    }
};

时间复杂度:O(n),空间复杂度:O(1)。

3.2 杨辉三角

力扣链接:118. 杨辉三角

题目描述:

3.2.1 C语言太麻烦!沟槽的二级指针还在追我!

麻不麻烦?麻烦!我们如果用C语言来实现的话,就要动态开辟二维数组——开辟指针数组,这个指针数组是指向数组的指针,或者这样理解:这个数组里面存放了指向数组的指针。

3.2.2 C++的vector——轻松搞定

不用指针,用容器怎么表示二维数组呢?vector<vector<int>>——

3.2.3 算法代码实现

代码演示如下——

代码语言:javascript
复制
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv;
        //定义行
        vv.resize(numRows,vector<int>());
        //定义列
        for(size_t i = 0;i < numRows;++i)
        {
            vv[i].resize(i + 1,1);//1
        }

        for(size_t i = 2;i < vv.size();++i)//第0、1行的都不需要处理,1
        {
            for(size_t j = 1;j < vv[i].size() - 1;++j)
            {
                vv[i][j] = vv[i-1][j] + vv[i-1][j-1];
            }
        }
        return vv;
    }
};

时间复杂度:O(n),空间复杂度:O(1)。


本文代码完整展示

(一)Test.c:

代码语言:javascript
复制
#define  _CRT_SECURE_NO_WARNINGS  1
#include<iostream>
#include<vector>
using namespace std;

void Print(const vector<int>& v)
{
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;

	////范围for
	//for (auto e : v)
	//{
	//	cout << e << " ";
	//}
	//cout << endl;
	//
	//vector<int>::const_iterator it = v.begin();
	//while (it != v.end())
	//{
	//	cout << "it" << " ";
	//	++it;
	//}
	//cout << endl;
}

void Test_vector1()
{
	//构造
	vector<int> v1;//空
	vector<int>v2(10, 1);
	vector<int>v3(v2.begin(),v2.end());//迭代器
	string s1("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
	vector<int>v4(s1.begin(), s1.end());
	vector<int>v5(v3);

	//vector<int> v6({ 1,2,3,4,5,6 });
	vector<int> v6 = { 1,2,3,4,5,6 };
	vector<int> v7 = { 1,2,3,4,5,6,1,1,1,1,1,1 };

	Print(v2);
	Print(v4);
	Print(v6);
	Print(v7);

	auto il = { 10,20,30,1,2,2 };
	for (auto e : il)
	{
		cout << e << " ";
	}
	cout << endl;
}

void Test_vector2()
{
	vector<int> v1;
	//count int n = 1000000000000;
	const int n = 100;
	/*v1.reserve(n);*/

	size_t old = v1.capacity();
	/*cout << v1.capacity() << endl;*/
	size_t begin = clock();
	for (size_t i = 0; i < n; i++)
	{
		v1.push_back(i);

		if (old != v1.capacity())
		{
			cout << v1.capacity() << endl;
			old = v1.capacity();
		}
	}
	size_t end = clock();
	cout << end - begin << endl;

	vector<int> v2;
	v2.resize(100.1);
	Print(v2);
}

void Test_vector3()
{
	vector<int> v1 = { 1,2,3,4,5 };
	v1.push_back(6);
	Print(v1);

	v1.insert(v1.begin(), 0);
	Print(v1);

	v1.insert(v1.begin() + 3, 0);
	Print(v1);

	v1.erase(v1.begin());
	Print(v1);

	v1.erase(v1.begin() + 3);
	Print(v1);
}

//struct在C++里面也是类
struct AA
{
	int _a1 = 1;
	int _a2 = 1;

	AA(int a1 = 1,int a2 = 1)
		:_a1(a1)
		,_a2(a2)
	{ }
};

////模版
//template<class T>
//class vector
//{
//private:
//	T* _a;
//	size_t _size;
//	size_t _capacity;
//};

void Test_vector4()
{
	AA aa1 = { 0,0 };
	vector<AA> v = { aa1,{1,1},{2,2},{3,3} };
	auto it = v.begin();
	while (it != v.end())
	{
		cout << it->_a1 << ":" << it->_a2 << endl;
		++it;
	}
	cout << endl;

	//传AA对象
	v.push_back(aa1);
	v.emplace_back(aa1);

	//传构造AA对象的参数
	v.emplace_back(1, 1);
	v.push_back({ 2,2 });

	it = v.begin();
	while (it != v.end())
	{
		cout << it->_a1 << ":" << it->_a2 << endl;
		++it;
	}
	cout << endl;
}

int main()
{
	//Test_vector1();
	//Test_vector2();
	/*Test_vector3();*/
	Test_vector4();

	return 0;
}

(二)运行


结尾

往期回顾:

【C++STL :string类 (二) 】从接口应用到内存模型的全面探索

【C++STL:string类(一)】最高效的STL学习法:从string类开始,教你如何阅读官方文档

结语:都看到这里啦!那请大佬不要忘记给博主来个“一键四连”哦!

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++的两个参考文档
  • 1 ~> vector容器
    • 1.1 vector的接口数比string类少
    • 1.2 vector的介绍
    • 1.3 vector的理解
    • 1.4 vector的定义
  • 2 ~> vector的使用:结合文档
    • 2.1 没有输入输出流,无法cout——自己Print
    • 2.2 迭代器:vector iterator 的使用
      • 2.2.1 概念理解
      • 2.2.2 实践
    • 2.3 vector的空间增长问题
      • 2.3.1 文档接口理解
      • 2.3.2 不同环境下capacity增长倍数问题
      • 2.3.3 reserve:提前将空间设置足够,提高效率
      • 2.3.4 实践
    • 2.4 vector的增删查改
      • 2.4.1 vector也只有尾插和尾删
      • 2.4.2 insert && erase实践
    • 2.5 如果你觉得库里面的不好用可以用自己定义的模版
    • 2.6 简单了解一下emplace
      • 2.6.1 简单了解
      • 2.6.2 对比
      • 2.6.3 实践
  • 3 ~> vector实践:两道算法题
    • 3.1 只出现一次的数字
      • 3.1.1 代码实现
    • 3.2 杨辉三角
      • 3.2.1 C语言太麻烦!沟槽的二级指针还在追我!
      • 3.2.2 C++的vector——轻松搞定
      • 3.2.3 算法代码实现
  • 本文代码完整展示
    • (一)Test.c:
    • (二)运行
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档