返回顶部

C++ vector 学习笔记

std::vector

以下内容大多来自此处,并将其中内容做了简化,想详细了解的可点击该链接进行了解和学习。

定义于头文件<vector>

std::vector是封装动态数组的顺序容器。连续存储元素,所以不仅可以通过迭代器,还能用指向元素的常规指针访问元素。

vector的存储是自动管理的,因为要分配更多内存以管理将来的增长,所以通常占用多于静态数组的空间。扩容只在额外内存耗尽时重分配。分配的内存总量可用capacity()函数查询。可以通过调用shrink_to_fit()返回多出的内存给系统。若元素数量已知,则reserve()函数可用于消除重分配。

vector里面有一个指针指向一片连续的空间,当空间装不下的时候,会申请一片更大的空间,将原来的数据拷贝过去,并释放原来的旧空间。vector在动态增加大小的时候,不是在原有的空间上持续新的空间(无法保证原空间的后面还有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,并释放原空间。在VS下是1.5倍扩容,在GCC下是2倍扩容。

vector的常见操作复杂度如下:

  • 随机访问:\(O(1)\)
  • 在末尾插入或移除元素:均摊\(O(1)\)
  • 插入或移除元素:\(O(n)\)(与到vector结尾的距离呈线性关系)

vector的初始化方式(以int类型为例)

  • 不带参数的构造函数初始化

    std::vector<int>a;//初始化一个vector对象a,其大小为0
    
  • 带参数的构造函数初始化

    std::vector<int>a(10);//初始化一个vector对象a,其大小为10,初始值为默认值
    std::vector<int>a(10 , 1);//初始化一个vector对象a,其大小为10,初始值为1
    
  • 通过已有数组进行初始化

    int arr[] = {0 , 1 , 2 , 3 , 4 , 5};//定义一个大小为6的数组arr
    std::vector<int>a(arr + 1 , arr + 6);//初始化一个vector对象a,其大小为5,其初始值为{1 , 2 , 3 , 4 , 5},注意,这里的区间是左闭右开的
    
  • 通过已有vector进行初始化

    std::vector<int> b = {1 , 2 , 3 ,4};//初始化一个vector对象,其初始值为{1 , 2 , 3 ,4}
    std::vector<int>a(b);//定义一个vector对象,并用b进行初始化
    //考虑上一个方法,此方法等价于
    std::vector<int>a(b.begin() , b.end());
    

vector的赋值方式

std::vector<int>b = {1 , 2 , 3 , 4};//初始化一个vector对象,初始值为{1 , 2 , 3 , 4}
std::vector<int>a = b;//定义一个vector对象a并将b的值赋值给a
//假设已有对象a,可通过下述方式进行修改
a = std::vector<int>(10 , 0);//此时a的元素由{1 , 2 , 3 , 4}变成10个0
a.assign(10 , 0);//等价于上一条语句

多维vector定义

using std::vector;//在没有使用using namespace std时,使用该语句可以在声明vector时不用加前面的std::
vector<vector<int>>a;//定义一个二维的vector对象,其大小为0
vector<vector<int>>b(10);//定义一个二维的vector对象,其大小为10,每行是一vector对象,其大小为0
vector<vector<int>>c(10 , vector<int>(10 , 0));//定义一个二维vector对象,其行和列的初始大小都为10
//更高维依次类推即可

成员函数

元素访问

  • at(size_type pos)

    返回位于指定位置pos的元素的引用,有边界检查。若pos不在容器范围内,则抛出std::out_of_range类型的异常,例如:

    a.at(1);//返回2对应的引用
    a.at(3);//返回4对应的引用
    a.at(5);//抛出越界异常
    
  • operator[]

    返回位于指定位置pos的元素的引用,不进行边界检查,可以类比定义的数组直接使用下标的方式进行访问。

    a[1];//结果同上
    a[5];//发送越界异常
    
  • front()

    返回容器首元素的引用,在空容器上使用front()是未定义行为。

    a.fornt();//返回1对应的引用
    
  • back()

    返回容器尾元素的引用,在空容器上使用back()是未定义行为。

    a.back();//返回5对应的引用
    

以上函数的访问效率都是\(O(1)\)的。

容量

以下函数以std::vector<int> a = {1 , 2 , 3 , 4 , 5};为例

  • empty()

    检查容器是否无元素,即begin() == end()

    a.empty();//调用方式
    
  • size()

    返回容器中的元素数量。注意:此方法返回的是无符号整数,使用过程中进行-1操作时注意是否产生溢出。

    a.size();//返回5
    
  • max_size()

    返回根据系统或库实现限制的容器可保有的元素最大数量。通常反映容器大小的理论极限。

以上函数的访问效率都是\(O(1)\)的。

迭代器

以下函数以std::vector<int> a = {1 , 2 , 3 , 4 , 5};为例

  • begin()/cbegin()

    返回指向vector首元素的迭代器。若vector大小为\(0\),则返回的迭代器等于end()

    a.begin();//指向1的迭代器
    //想要得到1这个值可以使用如下方法
    *a.begin();
    
  • end()/cend()

    返回指向vector末元素后一元素的迭代器。此元素表现为占位符不能使用*a.end()去访问其元素值。

  • rbegin()/crbegin()

    返回指向逆向vector首元素的迭代器。

    a.rbegin();//指向5的迭代器
    *a.rbegin();//该迭代器的值
    
  • rend()/crend()

    返回指向逆向vector尾元素的后一个元素,也是占位符。

以上函数的访问效率都是\(O(1)\)的。

其他成员函数

  • clear()

    清除容器中的所有元素。调用该方法后size()的返回值为\(0\)。注意:此方法保持vectorcapacity()不变。即:

    int sizePre = sizeof(a);//清除前的vector的大小
    a.clear();//调用clear()
    int sizeNext = sizeof(a);
    sizePre == sizeNext;//此表达式的值为true
    

    虽然原有的空间还在,但使用下标去访问非法。

  • insert()

    该函数有好几个重载,可以参考此处进行学习

    复杂度是线性的。

  • erase()

    有两个重载,分别是移除位于pos的元素和移除范围[first , last)中的元素。

    具体可参考此处进行学习

    复杂度也是线性的。

  • push_back()

    将给定元素添加到容器末尾。

  • emplace_back()

    在容器尾就地构造元素。就平时使用而言与push_back()方法没区别。

  • pop_back()

    移除末尾的元素。

  • resize()

    改变容器中可存储元素的个数。可以参考此处学习

  • swap(vector& other)

    将内容与 other 的交换。不在单独的元素上调用任何移动、复制或交换操作。所有迭代器和引用保持合法。尾后迭代器被非法化。

    复杂度是\(O(1)\)的。调用std::swap(a , b)进行交换,在c++98的复杂度是线性的,在c++11中对该函数进行了特化,std::swap(vector& lhs , vector& rhs)等价于lhs.swap(rhs),复杂度\(O(1)\)

使用一些algorithm库函数

以下内容使用vector<int>a = {1 , 2 , 3 , 3 , 4 ,5 , 8 , 7 , 9}为例。

  • 快速排序sort

    std::sort(a.begin() , a.end());//将区间[0 , 9)的元素排序
    std::sort(a.begin() + 1 , a.end());//将区间[1 , 9)的元素排序
    std::sort(a.begin() + 1 , a.begin() + 5);//将区间[1 , 5)的元素排序
    
  • lower_bound

    该函数返回第一个大于等于target的元素的迭代器,使用该函数前数组要有序。

    std::lower_bound(a.begin() , a.end() , target);//返回对应的迭代器,若没有大于等于target的元素则返回a.end()
    int idx = std::lower_bound(a.begin() , a.end() , target) - a.begin();//返回该迭代器对应的元素的下标
    
  • upper_bound

    该函数返回第一个大于target的元素的迭代器,使用该珊瑚前数组要有序。

    std::upper_bound(a.begin() , a.end() , target);//返回对应的迭代器,若没有大于target的元素则返回a.end()
    int idx = std::upper_bound(a.begin() , a.end() , target) - a.begin();//返回该迭代器对应的元素的下标
    
  • 去重

    将数组中的重复元素去除,本质是将重复的元素放在数组末尾。去重前数组要有序。

    std::sort(a.begin() , a.end());//先排序
    int n = std::unique(a.begin() , a.end()) - a.begin();//n我去重后的元素数量
    //使用上述语句后,a = {1 , 2 , 3 , 4 , 7 , 8 , 9 , 3} n = 7
    
posted @ 2022-04-15 10:58  cherish-lgb  阅读(89)  评论(0编辑  收藏  举报