【第十七课】STL容器:vector

目录

前言

原理介绍

vector相对普通数组的优势

常用内置函数

一些解释 

两个vector的比较运算


 

前言

在前面学习的过程中,其实vector用的还是挺多的。我们所使用到的vector最主要的功能就是,利用vector创建动态数组,也就是可以不提前规定数组的大小, 系统自动根据我们所使用的空间来动态的管理我们数组的大小。

原理介绍

vector实现动态数组的原理是:倍增。也就是我们vector类型的数组,当 当前空间不够用的时候,就会直接创建一个空间大小是原来2倍的数组,将原来空间里的元素复制到新的数组中

关于当前空间的说法,是否与我们所说的“vector可以不提前规定数组的大小”存在冲突呢?答案是不会的。

对于vector类型数组的“当前空间”指的是:如果我们在创建vector时没有指定大小,那么它的初始大小通常是0。也就是说,vector在刚被创建时并不会预先分配任何内存,只有当我们第一次向其添加元素时,它才会开始分配内存

同时,我们之前用到的vector还有一个优势,就是vector容器创建的数组可以由函数直接作为返回值将数组返回到主函数中。

使用vector创建的格式

    vector a;//创建一个数据元素为int类型的数组,数组名称是a
    vector a(10);//在原有基础上,规定了该数组的元素个数是10个
    vector a(10,2);//在原有基础上,规定该数组的这10个元素都初始化为2

    vector a[10];//创建了10个vector类型的数组

(最后一个数组类型还没怎么用过,前面的比较常见)  

vector相对普通数组的优势

于是这里我们会发现其实vector实现动态管理数组大小的方式好像是和普通数组想要扩容的方式是一样的(都是把原来的数据复制到更大的空间里),那vector好在哪里呢?不也是一样复杂?

关键点就在这里。我们说系统为程序分配空间,与所要分配空间的大小无关,与请求分配的次数有关。那我们就来分析一下二者扩容的差别

在普通数组中,每次的扩容操作的大小是不确定的,可能会因为容量不足而导致多次复制整个数组,这样的情况在极端情况下(每次只能扩容一个元素)可能会导致总体时间复杂度达到 O(n^2)

但对于vector来说,每次倍增扩容。向vector中添加n个元素的总时间复杂度仍然是O(n),vector需要进行的内存分配次数大约是logn,虽然扩容操作的次数可能是以指数级别增长的,但由于每次扩容的成本被分摊到每次插入操作上,插入一个元素的平均时间复杂度仍然是 O(1)总的时间复杂度是O(n)

关于分摊的这个说法,我也是久久不能透彻的理解。这里是gpt给出一些例子,可以帮助理解个大概。

vector数组: 

【第十七课】STL容器:vector_第1张图片

普通数组:

【第十七课】STL容器:vector_第2张图片

常用内置函数

这两个是一般容器里都会有的 

//时间复杂度O(1)
    a.size();//返回 a数组中元素的个数
    a.empty();//返回 a数组 是不是空的:是空的 返回1 不是空的返回0

下面是vector一些独特的

//时间复杂度O(1)
    a.size();//返回 a数组中元素的个数
    a.empty();//返回 a数组 是不是空的:是空的 返回1 不是空的返回0
    a.front();//返回vector的第一个数
    a.back();//返回vector的最后一个数
    a.pop_back();//删除vector的最后一个数
    a.begin();//vector的第0个数
    a.end();//vector的最后一个数后面的那个数
    a.push_back(1);//向vector末尾的数后面添加一个数  
                   //注意这个插入函数在写的时候把要插入的值写在括号里
   //平均时间复杂度是O(1),但是当vector的容量不足以容纳新的元素时,需要进行重新分配内存和复制元素,此时的时间复杂度是O(n)

//时间复杂度O(n)
    a.clear();//清空,删除vector中的所有元素。执行这个函数后,vector的大小(size())将变为0
                

一些解释 

关于下面这两个

a.begin();//vector的第0个数 
a.end();//vector的最后一个数后面的那个数

这两个函数返回的是迭代器,它们经常用来遍历vector中的所有元素

实际上 a.begin() 就是 a[0]  , a.end() 就是  a[a.size()]

关于迭代器,先浅浅知道他是个什么东西:可以把它看成一个指针,它可以指向容器中的一个元素,然后可以移动到下一个元素。 (要注意利用它遍历时,变量的输出要解引用*

a.begin(); 返回的是指向vector第一个元素的迭代器,a.end(); 返回的是指向vector最后一个元素之后的位置的迭代器,通常这个位置并没有实际的元素。这样设计的目的是为了方便使用如下的循环结构来遍历vector: 

for(auto it = a.begin(); it != a.end(); ++it) {
    // 使用 *it 可以访问当前元素
}

使用auto可以自动识别元素类型。

这里it表示的是迭代器,如果要明确按他的类型来定义的话应该是

for(vector::iterator it = a.begin(); it != a.end(); ++it) {
        cout<<*it<<" ";
    }

iterator就是迭代的意思。

这样写第一个表达式的含义就是:声明一个类型为 vector::iterator 的变量 it ,并将它初始化为a的起始位置的迭代器。 

两个vector的比较运算

vector的大小比较,有两套不同的比较系统,且他们都是成立的,因此会出现按照不同方法比较导致的比较结果不同的情况。

1.比较长度

直接比较两个vector的长度,即元素的数量:若 a.size() > b.size()  ,我们可以说 a>b

2.比较内容

按照元素的顺序,逐个比较两个vector中对应的元素,比较的方式和字符串的比较相似,都是根据字典顺序比较


好了,今天先写到这里。白天有些事情冲突了,今天就学的比较少啦。明天继续!

有问题欢迎指出!一起加油!! 

你可能感兴趣的:(算法--学习笔记,c++,算法)