首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C++ Vector:动态数组的高效使用指南

C++ Vector:动态数组的高效使用指南

作者头像
用户11831438
发布2025-12-30 14:03:12
发布2025-12-30 14:03:12
3160
举报
一、初识Vector:C++的动态数组容器

Vector 本质上是一个能够动态增长的数组,它在保留普通数组随机访问高效这一核心优势的同时,解决了其固定容量、不够灵活的痛点。通过自动管理内存和自动扩容的机制,它让你可以像使用无限延伸的数组一样,无需关心底层内存分配,就能高效、便捷地存储和访问一系列元素。

vector的核心特性

  • 动态数组:底层在连续的内存空间中存储元素。
  • 随机访问:通过 [] 运算符或 at() 方法,可以在 O(1) 时间复杂度内访问任何元素。
  • 动态扩容:当插入新元素导致当前容量不足时,vector 会自动申请一块更大的内存(通常是原大小的1.5或2倍),将原有元素拷贝移动到新内存,并释放旧内存。
  • 尾部操作高效:在末尾插入(push_back)或删除(pop_back)元素,时间复杂度为 O(1)(不考虑扩容开销)。
  • 中间/头部操作低效:在中间或头部插入或删除元素,需要移动后续的所有元素以保持连续性,时间复杂度为 O(n)
二. vector核心接口:必学的几个高频操作
  • 我们有了前面学习 string 的基础,再来学习 vector 就轻松很多了,而且也不需要记住所有接口,重点掌握 “构造、迭代器、空间管理、增删查改” 四大类核心接口,即可覆盖 90% 以上场景。

vector是一个很奇怪的容器,它的里面没有实现流插入和流提取,所以我们就需要自己书写一个打印的函数(很简单的),话不多说,直接上代码:

  • vector.cpp
代码语言:javascript
复制
void print(const vector<int>& v)
{
	//下标+[]
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	//迭代器
	/*vector<int>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}*/
	////范围for
	//支持迭代器的都支持范围for
	/*for (auto e : v)
	{
		cout << e << " ";
	}*/
}
2.1 定义和初始化(构造函数)

(constructor)构造函数声明

接口说明

vector()(重点)

无参构造

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

构造并初始化n个val

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

拷贝构造

vector (InputIterator first, InputIterator last);

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

以及C++11中一个比较好用的一个构造:

ok,接下来我们一一来看:

  • vector() 无参构造
代码语言:javascript
复制
//无参构造
vector<int> v;
  • vector(size_type n, const value_type& val = value_type())构造并初始化n个val
代码语言:javascript
复制
//创建10个数据,10个数据都是1
vector<int> v1(10, 1);
  • vector (const vector& x); 拷贝构造
代码语言:javascript
复制
//拷贝构造
vector<int> v1(10, 1);
vector<int> v2(v1);
  • vector (InputIterator first, InputIterator last); 使用迭代器进行初始化构造

这里使用迭代器进行初始化,可以传vector类型,也可以传其他对象的迭代器(前提:类型之间可以进行转换)

1、传vector类型

代码语言:javascript
复制
//使用迭代器空间初始化
vector<int> v1(10, 1);
vector<int> v3(v1.begin(), v1.end());

2、传其他类型的对象(stirng类)

代码语言:javascript
复制
//使用迭代器空间初始化
string s("hello world");
vector<int> v3(s.begin(), s.end());

接下来,我们一起来看看C++11中给我们提供的一个特殊构造: 1、用法

代码语言:javascript
复制
vector<int> v4 = { 1,2,3,4,5,6,7,8,9,10 };
//更严格的写法
vector<int> v4({ 1,2,3,4,5,6,7,8,9,10 });

这时候就有uu想问了,为什么可以这样写?它的底层逻辑是啥? 2、底层逻辑

在C++11中,用了 { } ,编译器就会自动认为是initializer_list,我们可以使用 { } 括任意数量的值去初始化vector对象

底层原理: 内部有两个指针,一个指针指向开头,一个指针指向结尾,可以认为是内部开了一块空间把数组存下来了。

嗯?啥意思?有点不太理解

ok,我们来看下面这张图上的内容:

我们把数组传给initializer_list中的参数il,这个参数其实是创建了一个对象,然后这个对象用指针指向这个数组的开始和结尾,内部底层就相当于写了一个范围for,然后这个范围for就遍历这个initializer_list中的参数il构造对象,然后把它给push_back到vector中

2.2 迭代器:遍历 vector 的 "万能工具"
  • 迭代器是访问容器元素的通用接口,vector 的迭代器本质是 “封装的指针”,支持遍历、取值、移动等操作。

iterator的使用

接口说明

begin + end(重点)

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

rbegin + rend

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

  • begin + end(重点)

begin()是指向数据开头的迭代器,end()是指向最后一个有效数据的下一个位置

  • 代码演示:
代码语言:javascript
复制
void testVector3()
{
	vector<int> v({ 1,2,3,4,5,6,7,8,9,10 });
	auto it = v.begin();//指向开头数据的迭代器,也就是指向v[0]
	while (it != v.end())//end()是指向最后一个数据的下一个位置的迭代器
	{
		cout << *it << " ";
		++it;
	}

}
  • rbegin + rend

rbegin 和 rend 正好与begin 和end相反。rbegin是指向最后一个数据的迭代器,end是指向开头数据的前一个位置的迭代器。

  • 代码演示:
代码语言:javascript
复制
void testVector3()
{
	vector<int> v({ 1,2,3,4,5,6,7,8,9,10 });
	auto it2 = v.rbegin();//指向最后一个数据的迭代器,也就是指向v[9]
	while (it2 != v.rend())//rend()是指向开头数据的上一个位置的迭代器
	{
		cout << *it2 << " ";
		++it2;
	}
}
2.3 空间管理:size、capacity 与扩容策略(reserve)
  • vector 的 “空间” 分为 size(有效元素个数)和 capacity(最大可容纳元素个数,不含结束标志),理解两者区别是避免扩容开销的关键。

核心接口:

容量空间

接口说明

size

获取数据个数

capacity

获取容量大小

empty

判断是否为空

resize(重点)

改变vector的size

reserve (重点)

改变vector的capacity

  • size

获取有效数据个数

代码语言:javascript
复制
void testVector3()
{
	vector<int> v({ 1,2,3,4,5,6,7,8,9,10 });
	size_t size=v.size();//求出有效数据个数
	cout << size << endl;
}
  • capacity

获取空间大小

代码语言:javascript
复制
void testVector3()
{
	vector<int> v({ 1,2,3,4,5,6,7,8,9,10 });
	size_t capacity = v.capacity();
	cout << capacity << endl;
}
  • empty

判断是否为空

代码语言:javascript
复制
void testVector4()
{
	vector<int> v({ 1,2,3,4,5,6,7,8,9,10 });
	bool ret = v.empty();
	cout << ret << endl;
	if (!ret)
	{
		cout << "非空" << endl;
	}
	else
	{
		cout << "空的" << endl;
	}
}
  • resize(重点)

调整 size 到 n(缺省用 0 填充)

1、开空间并插入值

代码语言:javascript
复制
void testVector4()
{
	vector<int> v;
	//开10个空间,没有传要插入的值,默认插入0
	v.resize(10);
	for (auto e : v)
	{
		cout << e << " ";
	}

	vector<int> v1;
	//开10个空间,插入1
	v1.resize(10, 1);
	for (auto e : v1)
	{
		cout << e << " ";
	}
}

2、reszie也可以进行缩容操作,如果n<size(),就保留前n个数据

代码语言:javascript
复制
void testVector()
{	
    vector<int> v2({ 1,2,3,4,5,6,7,8,9,10 });
    //5<v2.size() 保留前5个
	v2.resize(5);
	for (auto e : v2)
	{
		cout << e << " ";
	}
}

resize真正的用途是——开空间并插入值,如果对象中有数据,则在后面继续添加(情况很少)!!!

  • reserve (重点)

改变capacity

代码语言:javascript
复制
void testVector5()
{
	vector<int> v({ 1,2,3,4,6 });
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << v.capacity() << endl;
	cout << endl;
	//现在我想再插入5个数据,但是空间不够了,需要扩容
	//将空间扩到10
	v.reserve(10);
	v.push_back(5);
	v.push_back(7);
	v.push_back(8);
	v.push_back(9);
	v.push_back(10);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << v.capacity() << endl;
}

关键:vector 扩容策略

vector 扩容时会分配新内存、迁移旧元素、释放旧内存,这个过程耗时较高。不同编译器扩容倍数不同:

  • VS :1.5 倍扩容(如 capacity 从 4→6→9→13...);
  • G++ :2 倍扩容(如 capacity 从 4→8→16→32...)。

reserve的真正使用场景:提前开空间,减少扩容次数,提高效率

注意:在vector中,reserve是要多少空间就开多少空间,不会多开空间

2.4 增删查改:日常开发高频操作
  • vector 的增删查改接口设计简洁,重点关注 “尾插 / 尾删” 的高效性和 “中间插入 / 删除” 的注意事项。

vector增删查改

接口说明

push_back(重点)

尾插

pop_back (重点)

尾删

insert

在position之前插入val

erase

删除position位置的数据

operator[] (重点)

像数组一样访问

当然还有 emplace 和 emplace_back等接口,后面会一一介绍~~~

  • push_back(重点)

在尾部插入数据

代码语言:javascript
复制
void testVector6()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	for (auto e : v)
	{
		cout << e << " ";
	}
}
  • pop_back (重点)

删除尾部数据

代码语言:javascript
复制
void testVector6()
{
	vector<int> v;
	//尾插
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	//尾删
	v.pop_back();
	v.pop_back();
	for (auto e : v)
	{
		cout << e << " ";
	}
}

执行结果:

vector中没有提供头插和头删的接口,这是因为头插和头删的时间复杂度都是O(n),不建议使用头插和头删,但是可以使用insert和erase

  • insert

在任意位置插入数据,insert用迭代器传位置

代码语言:javascript
复制
void testVector6()
{
	vector<int> v;
	//尾插
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	//头插
	v.insert(v.begin(), 0);
	//在中间插入
	v.insert(v.begin()+2, 10);
	for (auto e : v)
	{
		cout << e << " ";
	}
}

执行结果:

  • erase

在任意位置删除数据,erase用迭代器传位置

代码语言:javascript
复制
void testVector6()
{
	vector<int> v;
	//尾插
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	//头删
	v.erase(v.begin());
	//删除中间位置数据
	v.erase(v.begin()+2);
	for (auto e : v)
	{
		cout << e << " ";
	}
}

执行结果:

  • operator[] (重点)

vector中有operator[] 这个接口,我们就可以像数组一样使用下标+[ ] 的方式修改和遍历vector对象

代码语言:javascript
复制
void testVector7()
{
	vector<int> v({ 1,2,3,5,6,7,8,9 });
	//遍历
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	//修改下标为0的数据
	v[0] = 0;
	//下标+[]
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;

	//遍历+修改
	for (size_t i = 0; i < v.size(); i++)
	{
		v[i]++;
		cout << v[i] << " ";
	}
}
  • emplace

emplace==insert

代码语言:javascript
复制
void testVector8()
{
	vector<int> v;
	//尾插
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	//头插
	v.emplace(v.begin(), 0);
	//在中间插入
	v.emplace(v.begin() + 2, 10);
	for (auto e : v)
	{
		cout << e << " ";
	}
}
  • emplace_back

emplace_back==push_back

但是他们两个还是有些区别的,我们一起来看一下~

ok,我们先来看一段代码:

为什么上面这段代码无法运行?这是因为AA中不支持流插入

那我们该怎么解决这个问题呢?

解决方法:

  1. 让AA支持流插入
  2. 用->

这时候就有UU想问了,为什么可以这样用? 当迭代器指向的数据是这种符合类型的类,数据是公有的,可以使用 -> ,可以认为底层的指针是数组AA的指针

ok,这时候又有UU想说:嗯?不是要说emplace_back吗?怎么说这个,这是因为emplace_back只有在这种情景下,才能说清和push_back的区别。

差异:

ok ,在现阶段中,我们就认为emplace_back和push_back 是一样的就好了~~~

三. vector 实战:OJ 算法题中的高频用法
  • vector 是算法刷题的 “主力容器”,以下 2 道经典 OJ 题,覆盖 vector 的核心使用场景。
3.1 只出现一次的数字
  • 题目链接:

https://leetcode.cn/problems/single-number/

  • 题目描述

  • 算法代码
代码语言:javascript
复制
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int tmp=0;
        for(auto e:nums)
        {
            tmp^=e;
        }
        return tmp;
    }
};
3.2 杨辉三角
  • 题目链接

https://leetcode.cn/problems/pascals-triangle/

  • 题目描述

  • 算法代码
代码语言:javascript
复制
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        //创建一个vector<vector<int>>对象
        vector<vector<int>> vv;
        //行,匿名对象初始化
        vv.resize(numRows,vector<int>());
        //列
        for(size_t i=0;i<numRows;i++)
        {
            vv[i].resize(i+1,1);
        }
        for(size_t i=2;i<vv.size();i++)
        {
            for(size_t j=1;j<vv[i].size()-1;++j)
            {
                vv[i][j]=vv[i-1][j-1]+vv[i-1][j];
            }
        }
        return vv;
    }
};
  • 题目解析

要真正的掌握这道算法题,我们就要对vector的底层有一点的了解(后面会介绍的vector的底层),我们vector其实和我们在数据结构中学的顺序表的是差不多的,既然是这样的话,那我们是不是就可以写出vector的私有成员:

代码语言:javascript
复制
class vector<int>
{
private:
	int* _a;
	size_t _size;
	size_t _capacity;
};

那我们是怎么构造出一个二维数组的呢?

  • 先用int 实例化出vector<int>的类

  • 再用vector<int>作为模板参数去实例化出vector<vector<int>>的类

用vector<vector<int>>实例化出的对象中的数组中的成员类型都是vector<int>,vector<int>实例化出的对象中的数组是一个成员类型都是int的数组。

通过上面的了解,我们就知道这个二维数组是怎么实现的了~~~

ok,那接下来我们来看看是怎么访问数据的:

上图所演示的又是怎么完成的呢?

我们先来看看这个两个 [ ] 运算符重载是啥样的: 1、

2、

对于二维数组的理解还是比较重要的!!!

结尾

ok,写到这里vector的使用基本上就已经结束了,看到这里的小伙伴给自己一个大大的👍~!!!

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、初识Vector:C++的动态数组容器
  • 二. vector核心接口:必学的几个高频操作
    • 2.1 定义和初始化(构造函数)
    • 2.2 迭代器:遍历 vector 的 "万能工具"
    • 2.3 空间管理:size、capacity 与扩容策略(reserve)
    • 2.4 增删查改:日常开发高频操作
  • 三. vector 实战:OJ 算法题中的高频用法
    • 3.1 只出现一次的数字
    • 3.2 杨辉三角
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档