《C++ STL:vector类(上)》:详解基础使用&&核心接口及经典算法题

🔥个人主页: Cx330🌸

❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》《优选算法指南-必刷经典100题》

🌟心向往之行必能至


🎬博主简介:

前言:

在vector类学习之前,大家可以回顾下string类的实现,可以发现我们string类的接口有很多,而vector类的接口比string少了很多,string类比STL诞生要早几年,STL很多容器和string都具有相似性,但是string的很多接口非常鸡肋


目录

前言:

一、vector容器

1.1vector的介绍

1.2对vector的理解

1.3vector的定义

二、vector的使用(结合文档)

2.1无输入输出流------自己实现Print

[2.2 迭代器:vector iterator 的使用](#2.2 迭代器:vector iterator 的使用)

[2.2.1 概念理解](#2.2.1 概念理解)

[2.2.2 实践](#2.2.2 实践)

[2.3 vector的空间增长问题](#2.3 vector的空间增长问题)

[2.3.1 文档接口理解](#2.3.1 文档接口理解)

[2.3.2 不同环境下capacity增长倍数问题](#2.3.2 不同环境下capacity增长倍数问题)

[2.3.3 reserve:提前将空间设置足够,提高效率](#2.3.3 reserve:提前将空间设置足够,提高效率)

[2.3.4 实践](#2.3.4 实践)

[2.4 vector的增删查改](#2.4 vector的增删查改)

[2.4.1 vector也只有尾插和尾删](#2.4.1 vector也只有尾插和尾删)

[2.4.2 insert && erase实践](#2.4.2 insert && erase实践)

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

[2.6 简单了解一下emplace](#2.6 简单了解一下emplace)

[2.6.1 简单了解](#2.6.1 简单了解)

[2.6.2 对比](#2.6.2 对比)

[2.6.3 实践](#2.6.3 实践)

三、vector实践:两道算法题

[3.1 只出现一次的数字](#3.1 只出现一次的数字)

代码实现

[3.2 杨辉三角](#3.2 杨辉三角)

C版本:

C++版本的vector------轻松搞定

算法代码实现

本文代码完整展示

Test.c:

结尾


一、vector容器

1.1vector的介绍

cplusplus中vector类介绍

1.2对vector的理解

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

1.3vector的定义

|-------------------------------------------------------------------------------------------------------|--------------|
| constructor)构造函数声明 | 接口说明 |
| (重点)vector( ) | 无参构造 |
| vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
| (重点)vector (const vector& x); | 拷贝构造 |
| vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |


二、vector的使用(结合文档)

2.1无输入输出流------自己实现Print

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

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

也可以范围for来实现

cpp 复制代码
	//范围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 实践
cpp 复制代码
	vector<int>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << "it" << " ";
		++it;
	}
	cout << endl;

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

cpp 复制代码
void Print(const vector<int>& v)
{
	//for (size_t i = 0; i < v.size(); i++)
	//{
	//	cout << v[i] << ' ';
	//}
	//cout << endl;

	//for (auto e : v)
	//{
	//	cout << e << ' ';
	//}
	//cout << endl;

	vector<int>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << ' ';
		++it;
	}
	cout << endl;
}

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

cpp 复制代码
void test_vector1()
{
	vector<int> v1;
	vector<int> v2(10, 1);
	vector<int> v3(v2);
	vector<int> v4(v3.begin(), v3.end());

	string s1("xxxxxxxx");
	vector<int> v5(s1.begin(), s1.end());

	vector<int> v6 = { 1,2,3,4,5 };
	Print(v2);
	Print(v5);
	Print(v6);

	auto il = { 1,2,3,4 };
	for (auto e : il)
	{
		cout << e << " ";
	}
	cout << endl;
}

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的默认扩容机制

cpp 复制代码
// 测试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倍方式扩容

cpp 复制代码
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

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

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

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

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

cpp 复制代码
// 如果已经确定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 实践
cpp 复制代码
void test_vector2()
{
	//vector<int> v1;
	//v1.reserve(100);
	//size_t old = v1.capacity();
	//cout << v1.capacity() << endl;
	//for (size_t i = 0; i < 100; i++)
	//{
	//	v1.push_back(i);
	//	if (old != v1.capacity())
	//	{
	//		cout << v1.capacity() << endl;
	//		old = v1.capacity();
	//	}
	//}

	vector<int> v1;
	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);
}

2.4 vector的增删查改

|-------------------------------------------------------------------------------------------------|-------------------------------|
| vector增删查改 | 接口说明 |
| push_back | 尾插 |
| pop_back | 尾删 |
| find | 查找(注意这个是算法模块实现,不是vector的成员接口) |
| insert | 在position之前插入val |
| erase | 删除position位置的数据 |
| swap | 交换两个vector的数据空间 |
| operator[ ] | 像数组一样访问 |

2.4.1 vector也只有尾插和尾删

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

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

2.4.2 insert && erase实践
cpp 复制代码
void test_vector3()
{
	vector<int> v1 = { 1,2,3 };
	v1.push_back(4);
	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);

}

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

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

2.6 简单了解一下emplace

2.6.1 简单了解

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

2.6.2 对比
2.6.3 实践
cpp 复制代码
void test_vector4()
{
	AA aa1 = { 0,0 };
	vector<AA> v1 = { aa1,{1,1},{2,2},{3,3} };
	auto it = v1.begin();
	while (it != v1.end())
	{
		cout << it->_a1 << ' ' << it->_a2 << endl;
		++it;
	}
	cout << endl;

	v1.push_back(aa1);
	v1.emplace_back(aa1);
	//差异
	v1.push_back({ 1, 1 });//既可以传AA对象,又可以传构造AA对象的参数
	v1.emplace_back(2, 2);//只能传AA对象

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

三、vector实践:两道算法题

3.1 只出现一次的数字

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

题目描述:

题目示例:

代码实现

代码演示如下

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) 
    {
       int val=0;
       for(auto e:nums)
       {
        val^=e;
       }     
       return val;
    }
};

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

3.2 杨辉三角

力扣链接:118. 杨辉三角

题目描述:

题目示例:

C版本:

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

C++版本的vector------轻松搞定

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

算法代码实现

代码:

cpp 复制代码
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:

cpp 复制代码
#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 (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);
	vector<int> v4(v3.begin(), v3.end());

	string s1("xxxxxxxx");
	vector<int> v5(s1.begin(), s1.end());

	vector<int> v6 = { 1,2,3,4,5 };
	Print(v2);
	Print(v5);
	Print(v6);

	auto il = { 1,2,3,4 };
	for (auto e : il)
	{
		cout << e << " ";
	}
	cout << endl;
}

void test_vector2()
{
	//vector<int> v1;
	//v1.reserve(100);
	//size_t old = v1.capacity();
	//cout << v1.capacity() << endl;
	//for (size_t i = 0; i < 100; i++)
	//{
	//	v1.push_back(i);
	//	if (old != v1.capacity())
	//	{
	//		cout << v1.capacity() << endl;
	//		old = v1.capacity();
	//	}
	//}

	vector<int> v1;
	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 };
	v1.push_back(4);
	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 AA {
	int _a1 = 1;
	int _a2 = 1;
	AA(int a1 = 1, int a2 = 1)
		:_a1(a1)
		, _a2(a2)
	{
	}
};

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

	v1.push_back(aa1);
	v1.emplace_back(aa1);
	//差异
	v1.push_back({ 1, 1 });//既可以传AA对象,又可以传构造AA对象的参数
	v1.emplace_back(2, 2);//只能传AA对象

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

//template<class T>
//class vector {
//paivate:
//	T* _str;
//	size_t size;
//	size_t capacity;
//};

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

结尾

往期回顾:

《一篇拿下C++:string类(详解版)》:教你如何从入门到避坑再到玩转字符串问题

总结:这篇博客到此为止,给大家分享了vector类的接口使用,那么下篇博客我将给大家分享vector的底层,如果这篇文章对你有帮助的话,可以给博主一键四连哦

相关推荐
那我掉的头发算什么2 小时前
【数据结构】二叉树的高频热门面试题大全
java·开发语言·数据结构·python·算法·链表·intellij idea
一人の梅雨2 小时前
买家秀接口深度开发:从内容解析到情感分析的全链路实现
开发语言·php
遇安.YuAn2 小时前
JAVA之求平方根
java·开发语言·算法
Thexhy3 小时前
在Centos的Linux中安装Windows10系统
linux·运维·经验分享·学习·centos
菜鸟plus+3 小时前
Captcha
java·开发语言
hqwest3 小时前
QT肝8天13--删除用户
开发语言·c++·qt·csdn开发云·列表分页·qt分页
聪明的笨猪猪3 小时前
Java 高并发多线程 “基础”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
禁默4 小时前
机器学习基础入门(第三篇):监督学习详解与经典算法
学习·算法·机器学习
李小白664 小时前
python 函数
开发语言·python