C++(九) (STL)Vector

1.STL

  • STL是什么呢?

        C++ 标准模板库(Standard Template Library,简称 STL)是 C++ 标准库的重要组成部分,提供了一套常用的模板类和函数,用于数据存储、操作和算法的实现。

        STL 是 C++ 强大的基础库,使得开发者能够高效、灵活地处理复杂的数据结构和算法。

        注:string在STL之前创造,所以严格来说string不属于STL,但是STL的容器设计的风格也参考了string(或说保持风格统一),所以在我们学习过程中会感觉STL的容器会有string的影子。 

1.1STL的组成

STL 通常被描述为由六大组成部分构成:

        1. 容器(Containers)

        2. 算法(Algorithms)

        3. 迭代器(Iterators)

        4. 仿函数(Functors)

        5. 配接器(适配器)(Adapters)

        6. 空间配置器(Allocators)

        接下来我们将依次学习 STL 的各个部分,以容器为大纲逐步深入学习各部分。

2.vector

  首先 什么是容器?什么是vector呢?

   简单点理解STL中用来储存数据的类就是容器。
   vector
是 C++ 标准模板库(STL)中的一种顺序容器。它实现了动态数组,能够自动调整大小并高效管理内存。std::vector 是 C++ 程序员最常用的容器之一,因为它既简单易用,又具备强大的功能。(可简单点理解成 vector就是一个动态数组,用vector这个类封装)

同样vector中详细的成员函数用法参考网站: cplusplus.com/reference/vector/vector/

2.1vector主要特点

  1. 动态大小

    • std::vector 可以自动调整其大小,当元素插入时,vector 会自动分配更多的内存,以容纳新元素。(自动扩容
    • 这与普通数组不同,普通数组在定义时必须指定大小且无法动态调整。
  2. 随机访问

    • std::vector 支持常数时间的随机访问,即可以通过索引快速访问任意元素,类似于数组。
    • 例如,vec[i] 可以快速访问第 i 个元素。(和C语言的数组一样方式访问
  3. 自动管理内存

    • std::vector 会自动管理内存的分配和释放,因此你不需要手动管理内存,也不容易出现内存泄漏的问题。(动态内存,不需要自己申请和释放
  4. 高效的插入和删除

    • std::vector 在末尾添加或删除元素的操作是常数时间复杂度 O(1),非常高效。(可频繁尾插尾删
    • 但在中间或开头插入或删除元素的操作可能会导致大量数据移动,时间复杂度为 O(n),不如链表高效。

        (vector底层实现是数据结构中顺序表,所以具备的特点也是顺序表的特点)

2.2 vector的使用

先看看一段代码:

#include <iostream>
#include <vector>    //使用vector需要包的头文件

int main() {
    // 创建一个空的 vector,vector中存放的数据类型是int,实例化为vec
    std::vector<int> vec;

    // 向 vec中添加元素
    vec.push_back(10);     // 添加 10
    vec.push_back(20);     // 添加 20
    vec.push_back(30);     // 添加 30

    // 使用索引访问和修改元素
    std::cout << "vec[0]: " << vec[0] << std::endl; // 打印第0个元素
    vec[0] = 100;                                   // 修改第0个元素为 100
    std::cout << "vec[0]: " << vec[0] << std::endl; // 打印修改后的第0个元素

    // 获取并打印 vec 的大小和容量
    std::cout << "vec.size: " << vec.size() << std::endl;         // 输出 3
    std::cout << "vec.capacity: " << vec.capacity() << std::endl; // 输出大于或等于 3

    // 遍历 vec 中的所有元素
    std::cout << "Elements in vec : ";
    for (size_t i = 0; i < vec.size(); ++i) 
    {
        std::cout << vec[i] << " ";
    }
    std::cout << std::endl;

    // 使用迭代器遍历 vec 中的所有元素
    std::cout << "Elements in vector (using iterator): ";
    for (auto it = vec.begin(); it != vec.end(); ++it) 
    {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 删除末尾元素
    vec.pop_back(); // 删除 30

    // 再次遍历并打印修改后的 vec 
    std::cout << "Elements in vector : ";
    for (auto it = vec.begin(); it != vec.end(); ++it) 
    {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}

运行结果:

vec[0]: 10
vec[0]: 100
vec.size: 3
vec.capacity: 3
Elements in vec : 100 20 30
Elements in vector (using iterator): 100 20 30
Elements in vector : 100 20

 

以上代码演示的是一个储存<int>类型数据的顺序表(vector),结合图形和代码一起看更加直观:

1.创建一个空vector

std::vector<int> vec;

2.尾插3个数据

vec.push_back(10);
vec.push_back(20);
vec.push_back(30);

 

3.修改和访问第0个数据

    vec[0] = 100;     

 4.打印数据表中数据的长度和容量的大小

    std::cout << "vec.size: " << vec.size() << std::endl;         
    std::cout << "vec.capacity: " << vec.capacity() << std::endl; 

根据数据结果来看,在vector底层运作,他是动态申请的容量和数据长度一样大的,所以图形:

5.用迭代器遍历vector

    std::cout << "Elements in vector (using iterator): ";
    for (auto it = vec.begin(); it != vec.end(); ++it) 
    {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

   vec.end() 返回的是指向 vector最后一个元素的下一个位置的迭代器。这个位置实际上是一个“哨兵”位置(sentinel),不存储有效数据,通常用来标识容器的末尾。

这最后一个哨兵卫不会计入capacity(容量)内 。和string相同,vector 的迭代器底层也是用指针来实现,所以begin()返回的迭代器就是vec[0]的地址,end()同理返回的也是地址,且vector 内数据的地址是连续的。

6.删除一个数据

  vec.pop_back(); // 删除 30

2.3进阶用法 

通过2.2的学习,大家会发现,vector其实和string还是有很多相似之处的。

2.3.1.vector构造函数:

        可以怎么样创建vector对象呢?常用的在以下列举出来了:(需注意的是第三种用法)

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

int main() 
{
	/*1*/
	vector<int> vec1;			// 创建一个空的 vector<int>

	/*2*/
	vector<int> vec2(5, 10);	// 创建一个包含 5 个 10 的 vector<int>,即 {10, 10, 10, 10, 10}
	vector<int> vec3(5);		// 创建一个包含 5 个默认值 0 的 vector<int>,即 {0, 0, 0, 0, 0}

	/*3*/   
    //template <class InputIterator> std::vector<T> vec(InputIterator first, InputIterator last);
    //使用 [first, last) 范围内的元素创建一个 vector。(用区间初始化,传的是迭代器 vector迭代器就是地址)
	int arr[] = { 1, 2, 3, 4, 5 };
	vector<int> vec4(arr, arr + 5);  // 使用数组中的元素构造 vector,即 {1, 2, 3, 4, 5}

	/*4*///拷贝构造
	vector<int> vec5(vec1);  // 复制 vec1 的内容到 vec2 中

	/*5*/
	vector<int> vec6 = { 1, 2, 3, 4, 5 };  // 直接使用初始化列表创建 vector

	/*6*///比较少用
	vector<int> vec7 = { 1, 2, 3 };
	vector<int> vec8(std::move(vec1));  // 移动 vec1 的内容到 vec2 中,vec1 被清空
}

2.3.2.vector各个常用成员函数:

        接下介绍的成员函数仅常用,并不是所有,如果想要看更详细的函数用法请参考vector章节开始位置的网址。

1.push_back():(尾插)
  • 功能:在 vector 的末尾添加一个元素。(用法同string的push_back)
2.pop_back():(尾删)
  • 功能:移除 vector 末尾的元素。
3.size()
  • 功能:返回 vector 中元素的数量。
4.capacity():
  • 功能:返回 vector 当前分配的存储容量(即不需要重新分配内存可以容纳的元素数量)。

        (因为上面2.2的代码已经演示过了这里不过多赘述)

5.empty():
  • 功能:检查 vector 是否为空
std::vector<int> vec;
//如果为空返回bool的true ,不为空返回false
//语法:bool empty() const;(库中声明)
if (vec.empty()) 
{
    std::cout << "Vector is empty." << std::endl;
}
6.clear()
  • 功能:移除 vector 中的所有元素,但不释放内存。
std::vector<int> vec = {1, 2, 3};
vec.clear();  // 清空 vec 中的所有元素
7.insert(): 
  • 功能:在指定位置插入元素。(如果所选下标有元素,将在该下标插入值,把原本在该下标及其后面的元素向后移动)
  • 语法
iterator insert(iterator pos, const T& value);  // 在 pos 位置插入 value(pos)是下标
iterator insert(iterator pos, size_t n, const T& value);  // 在 pos 位置插入 n 个 value
  • 使用: 
std::vector<int> vec = {1, 2, 4};
vec.insert(vec.begin() + 2, 3);  // 在开始2位置插入 3,也就是vec[2] vec 变为 {1, 2, 3, 4}
vec.insert(vec.begin(),5, 3);  // 在开始位置插入 3,vec 变为 {1, 2, 3, 4}
8.erase(): 
  • 功能:移除指定位置或范围内的元素。
  • 语法
iterator erase(iterator pos);  // 移除 pos 位置的元素(下标)
iterator erase(iterator first, iterator last);  // 移除 [first, last) 范围内的元素
  • 使用: 
​
iterator erase(iterator pos);  // 移除 pos 位置的元素(下标)
iterator erase(iterator first, iterator last);  // 移除 [first, last) 范围内的元素

​std::vector<int> vec = {1, 2, 3, 4};
vec.erase(vec.begin() + 2);  // 移除索引 2 位置的元素,vec 变为 {1, 2, 4}
vec.erase(vec.begin() ,vec.end());  // 移除全部元素
9.resize(): 
  •  功能:调整 vector 的大小。如果新大小大于当前大小,则用默认值或指定的值填充新元素。如果新大小小于当前大小则截断(超出新大小的元素将被移除)
  • 语法
void resize(size_t n);    //调整大小
void resize(size_t n, const T& value);//如果是扩容,那新开的空间初始化为value
  • 用法 
std::vector<int> vec = {1, 2, 3};
vec.resize(5);        // 调整大小为 5,vec 变为 {1, 2, 3, 0, 0}
vec.resize(7, 10);    // 调整大小为 7,vec 变为 {1, 2, 3, 0, 0, 10, 10}
vec.resize(2);        //调整大小为 2,vec 变为 {1, 2}
10.reserve()
  • 功能:预留足够的空间以容纳 n 个元素,避免在插入元素时频繁重新分配内存。
  • 语法
语法:void reserve(size_t n);

n:表示你要预留的最小容量(即要分配的内存大小)。如果 n 大于当前容量,vector 将重新分配内存来达到指定的容量。如果 n 小于或等于当前容量,则不会发生任何变化。 

  • 用法:
std::vector<int> vec;
vec.reserve(10);  // 预留空间以容纳 10 个元素

        注意: reserve 函数为 vector 分配至少 n 个元素的存储空间,而不会改变 vector 的大小(即不会影响 size() 的返回值)。这个函数只影响 vector 的容量(capacity(),不影响元素的数量。

reserve和resize的区别:

  1. reserve 只影响 capacity,不改变 size,它只是预留内存空间。
  2. resize 影响 sizecapacity,改变 vector 中元素的数量。如果 size 增大,resize 会插入新元素,如果 size 减小,resize 会删除多余的元素。

进阶代码演示:

#include <iostream>
#include <vector>
#include <algorithm>  // 包含标准算法,如 sort 和 reverse
using namespace std;
int main() {
    // 创建并初始化一个 vector
    vector<int> numbers = { 10, 20, 30, 40, 50 };

    // 使用迭代器遍历并输出 vector 的元素
    cout << "numbers: ";

    vector<int>::iterator it;  //定义一个vector<int>类型的迭代器 变量it
    for (it = numbers.begin(); it != numbers.end(); ++it)
    {
        cout << *it << " ";
    }
    cout << endl;

    // 在 vector 中插入元素
    numbers.insert(numbers.begin() + 2, 25);  // 在下标位置为 2 插入 25

    cout << "numbers.insert(numbers.begin() + 2, 25): ";
    for (const auto& num : numbers)     //范围for打印
    {
        cout << num << " ";
    }
    cout << endl;

    // 删除 vector 中的某个元素
    numbers.erase(numbers.begin() + 3);  // 删除下标位置为 3 的元素

    cout << "numbers.erase(numbers.begin() + 3): ";
    for (const auto& num : numbers)     //范围for打印
    {
        cout << num << " ";
    }
    cout << endl;

    // 使用标准算法对 vector 排序,sort是一种一种混合排序算法,它结合了 快速排序、堆排序 和 插入排序 的优点
    //sort在C++ 标准库中 std::sort
    sort(numbers.begin(), numbers.end(), greater<int>());  // 降序排序

    cout << "std::sort: ";
    for (const auto& num : numbers) //打印数组
    {
        cout << num << " ";
    }
    cout << endl;

    
    //find也是C++ 标准库中的函数:std::find
    vector<int>::iterator it1 = find(numbers.begin(), numbers.end(), 30);    // 查找 vector 中的元素,返回迭代器,也就是元素所在的位置(地址)
    if (it1 != numbers.end()) 
    {
        cout << "30 found at position: " << distance(numbers.begin(), it) << endl;
        //std::distance 函数:用于计算两个迭代器之间的距离,也就是从第一个迭代器 numbers.begin() 走到第二个迭代器 it 需要经过的元素数量。
        //这可以被看作是计算元素在 vector 中的位置(基于 0 的索引)。
    }
    else {
        cout << "30 not found." << endl;
    }

 
    //reverse 也在C++ 标准库中 std::reverse()标准库函数
   // 反转 vector 的元素顺序
    reverse(numbers.begin(), numbers.end()); 

    cout << "reverse (): ";
    for (const auto& num : numbers) //打印
    {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

输出结果:

numbers: 10 20 30 40 50
numbers.insert(numbers.begin() + 2, 25): 10 20 25 30 40 50
numbers.erase(numbers.begin() + 3): 10 20 25 40 50
std::sort: 50 40 25 20 10
30 not found.
reverse (): 10 20 25 40 50

        代码解析都在上面代码的注释里,这里就不再赘述。这段代码展示了如何使用 C++ 标准库中的 vector 容器和常用算法函数。通过使用iterator迭代器进行遍历、插入和删除操作,同时使用了 sortfindreverse 等算法函数,对 vector 进行排序、查找和反转操作。理解这些操作对于学习 std::vector 和STL组成部分之一的算法非常有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值