【STL】vector介绍及使用 {构造函数,迭代器,容量相关接口,增删查改;动态二维数组}

一、vector的介绍

在这里插入图片描述

  1. vector是表示可变大小数组的序列容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  3. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  4. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。

二、vector的使用

vector和string的底层都是顺序表,且由于STL库函数的设计互通,因此使用也类似。

所以相关函数的使用细节可以参考文章:【STL模版库】string类:常用接口函数

下面的内容只做简介,不再做详细介绍。

2.1 构造函数

在这里插入图片描述


2.2 迭代器

在这里插入图片描述

在这里插入图片描述


2.3 容量相关接口

在这里插入图片描述

  • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。

  • resize在开空间的同时还会进行初始化,影响size。

  • 相同的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。

在这里插入图片描述


2.4 增删查改

在这里插入图片描述

  • 下面重点介绍的find、sort和swap函数都是由算法模块实现的函数模版,并不是vector的成员接口。
  • 在使用前需要包含头文件<algorithm>,并展开std命名空间。

2.4.1 find

在这里插入图片描述

此函数模版的行为等效于:

template<class InputIterator, class T>
  InputIterator find (InputIterator first, InputIterator last, const T& val)
{
  while (first!=last) {
    if (*first==val) return first;
    ++first;
  }
  return last;
}
  • find仅仅是一个函数模版,会根据传入参数的不同类型实例化出适应各种类型的find函数。
  • 这里使用迭代器遍历容器,是因为迭代器是所有容器的通用遍历访问方法。
  • 使用引用传参避免发生拷贝构造,更高效。
  • 如果找到了,返回对应元素的迭代器;如果找不到,返回last;

其他查找函数

find_if

功能:返回范围中第一个满足指定条件的元素的迭代器

参数:共有3个,前两个参数是待查找的迭代器区间;最后一个参数可以是一个仿函数或是函数指针,该仿函数/函数用于条件判断。

同类函数:find_if_not,与find_if逻辑相反,参数和使用方法相同。

find_first_of

功能:在母序列中从前往后查找子序列,返回母序列中第一次出现子序列的首元素的迭代器

参数:共有5个,前两个参数是母序列迭代器区间;中间两个是子序列迭代器区间;最后一个参数可以是一个仿函数或是函数指针,该仿函数/函数用于条件判断。

同类函数:find_end,与find_first_of查找方向相反是从后往前查找,参数和使用方法相同。


2.4.2 sort

在这里插入图片描述

C++STL库中的sort接口底层采用快速排序实现。

#include <iostream>
#include <algorithm> //sort
#include <functional> //greater
using namespace std;

int main(){
	string s("hello412535");
    sort(s.begin(), s.end()); //第三个参数缺省默认排升序;
    //sort(s.begin(), s.end(), less<char>()); //也可以显示的传less也是排升序;
    for(auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    
	sort(s.begin(), s.end(), greater<char>()); //传greater是排降序;
    for(auto e : v1)
    {
        cout << e << " ";
    }
    cout << endl;
    return 0;
}

前两个参数同样是待排序的迭代器区间范围

最后一个参数可以是一个仿函数或是函数指针,该仿函数/函数用于排序过程中比较大小,控制升降序。

第三个参数缺省默认升序,传less<T>()是升序排序,传greater<T>()是降序排序。

注意:

  • 这里的greater和less是类模版。
  • 传入的less<char>()和greater<char>()是实例化出的模版类定义的匿名对象。
  • 要使用greater类模版需要包<functional>头文件

2.4.3 swap

在这里插入图片描述

此函数模版的行为等效于:

template <class T> void swap ( T& a, T& b )
{
  T c(a); a=b; b=c;
}
  • 由此可见,算法模块中实现的swap函数模版,采用的是创建临时变量,赋值交换的方法。仅仅适用于内置类型,而对于自定义类型会进行1次拷贝构造和2次赋值拷贝。尤其针对需要进行深拷贝的对象,这种交换方式的效率极低。

  • 因此,对于需要进行交换操作的自定义类型一般会自己实现成员交换函数,只对成员变量进行浅交换即可。


三、动态二维数组

vector不仅可以存储内置类型,还可以用于存储自定义类型如:vector<string>和vector<vector<int>>

3.1 vector<string>

vector<char> 能否替代 string?

回答:不能

  1. string字符串由’\0’结尾,而vector<char>不会。
  2. string类重载了流插入<<和流提取>>运算符,能更加方便的进行输入输出。
  3. string类中还提供了操作字符串的专用接口,如operator+=、字符串比较大小、to_string等等。
  4. 因此vector<char> 不能替代 string
int main(){
    vector<string> strv;
    string str1("张三");
    strv.push_back(str1);
    strv.push_back(string("李四")); //[1]
    strv.push_back("王五"); //[2]
    for(const auto &name : strv) //[3]
    {
        cout << name << endl;
    }
}

解释:

  • [1] 定义匿名对象,匿名对象的生命周期只在当前行。
  • [2] 由于string类中定义了一个单参构造函数string (const char* s);,所以常量字符串"王五"可以进行隐式类型转换,构造临时string对象。
  • [2] 类型转换形成的临时对象具有常性,而void push_back (const value_type& val);的参数类型是常引用,因此这种写法可行。
  • [3] 范围for中的临时变量的类型建议写成引用,这是因为如果是普通变量会进行频繁地拷贝构造(尤其针对自定义类型),效率极低。如果循环中不做修改建议加const进行保护。

3.2 vector<vector<int>>

以题目【Leetcode.118】杨辉三角为例:

在这里插入图片描述

题解:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv; //使用vector定义二维数组vv,vv中的每个元素都是vector<int>
        vv.resize(numRows); //[1]
        for(size_t i = 0; i<vv.size(); ++i)
        {
            vv[i].resize(i+1); //[2]
            vv[i].front() = 1;
            vv[i].back() = 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] + vv[i-1][j-1]; //[3]
            }
        }
        return vv;
    }
};

解释:

  • [1] 为vector<vector>对象开空间并初始化。

  • [1] 这是resize的函数原型:void resize (size_type n, value_type val = value_type());

  • [1] 第二个参数用value_type类型的匿名对象做缺省值,相当于调默认构造生成临时对象。

  • [1] resize函数内利用临时对象拷贝构造生成新对象。

  • [1] 因此执行过这条resize语句之后的结构为:设numRows==5
    在这里插入图片描述

  • [2] 为每个vector元素开空间并初始化,然后将每行的第一列和最后一列置为1。

  • [3] vector<vector>动态二维数组的访问:先后调用两个operator[]的重载函数,实现了二维数组的访问遍历。
    在这里插入图片描述

  • [3] 还记得普通二维数组的访问方式吗?二维数组在内存中和一维数组一样是线性连续存储的,编译器会先将两个二维下标换算成一个一维下标,然后才能通过解引用访问。

  • 最后生成的二维数组结构图示
    在这里插入图片描述

  • 关于二维动态数组的深拷贝问题我会在下一节进行详解:【STL模版库】模拟实现vector类模版6.2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

52Hertz-))

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值