• 大话STL第二期——机械先驱—vector


    书接上期,这一期我们开始讲解STL容器里面的“维克托”也是最常用的容器之一。

    文章目录

    vector的概述

    vector的使用 

    vector常用API介绍

    vector迭代器常用的成员函数介绍:

    vector的构造函数操作:

     vector的常用赋值操作:

     vector的大小容量操作:

    vector的插入和删除操作: 

     vector的数据存取操作:

     示例:

    vector常用的创建,循环插入,遍历:

    vector利用swap收缩空间:

    vector的遍历以及二维数组:

    使用算法对vector数组进行排序:

    vector存放自定义数据类型操作:

    vector的概述

    向量(Vector)是一个封装了动态大小数组的顺序容器。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。

    vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)

    本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
    vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
    因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。

    vector的使用 

    在学习STL容器时有众多相对应的接口,和许多算法,因此一定要学会看帮助手册以及文档。下面我将列出vector所有常用的接口,并与之对应给出示例帮助学习和记忆。

    1. 使用vector容器时需要添加头文件
    2. #include

    vector常用API介绍

    vector迭代器常用的成员函数介绍:

    begin()函数:

    1. 函数原型:
    2. iterator begin();
    3. const_iterator begin();
    4. 功能:返回一个当前vector容器中起始元素的迭代器。

    end()函数:

    1. 函数原型:
    2. iterator end();
    3. const_iterator end();
    4. 功能:返回一个当前vector容器中末尾元素的迭代器。
    5. 注意end()指向的是最后一个元素的下一个位置,所以访问最后一个元素

    rbegin()函数:

    返回指向最后一个元素的反向迭代器

    rend()函数:

    返回指向第一个元素之前一个位置的反向迭代器。

    vector的构造函数操作:

    1. vector v;//采用模板实现类实现,默认构造函数
    2. vector(v.begin(), v.end());//将v.begin()到 v.end()区间的元素拷贝给本身
    3. vector(n, elem);//构造函数,将n个elem元素拷贝给自身
    4. vector(const vector & vec);//拷贝构造函数
    5. eg使用第二个构造函数
    6. vector<int>v(arr, arr + sizeof(arr) / sizeof(int));

    示例:

    下面我会编写一个打印函数,以供后面所有示例去调用,不会再重复编写。

    1. void Show(vector<int>& v1)
    2. {
    3. vector<int>::iterator it;
    4. for (it = v1.begin(); it != v1.end(); ++it)
    5. {
    6. cout << *it << " ";
    7. }
    8. cout << endl;
    9. }
    10. void test02()
    11. {
    12. vector<int> v1(5, 100);//用5个100来构造自身,调用第三个拷贝构造
    13. Show(v1);
    14. vector<int>v2 = v1;//旧对象给新对象赋值,调用第四个拷贝构造
    15. Show(v2);
    16. vector<int>v3 (v1.begin(), v1.end());//区间构造,调用第二个拷贝构造
    17. Show(v3);
    18. }
    19. int main()
    20. {
    21. test02();
    22. return 0;
    23. }

     vector的常用赋值操作:

    1. assign(begin, end);//将(begin,end)区间的数据拷贝赋值给自身
    2. assign(n, elem);//将n个elem拷贝赋值给自身
    3. vector& operator=(const vector & vec);//重载等号赋值运算符
    4. swap(vec);//将vec与本身元素互换

    示例: 

    1. void test02()
    2. {
    3. vector<int> v5 = { 1,2,3,4,5 }; //列表初始化,注意使用的是花括号
    4. vector<int> v1(5, 100);//用5个100来构造自身,调用第三个拷贝构造
    5. Show(v1);
    6. vector<int>v2 = v1;//旧对象给新对象赋值,调用第四个拷贝构造
    7. Show(v2);
    8. vector<int>v3 (v1.begin(), v1.end());//区间构造,调用第二个拷贝构造
    9. Show(v3);
    10. vector<int> v4;
    11. v4 = v3;//使用重载的赋值运算符
    12. Show(v4);
    13. v4.assign(5, 10);//给v4赋5个10
    14. Show(v4);
    15. v4.assign(v1.begin(), v1.end());//区间赋值
    16. Show(v4);
    17. }
    18. int main()
    19. {
    20. test02();
    21. return 0;
    22. }

     vector的大小容量操作:

    1. vector大小容量操作
    2. size();//返回容器中元素个数
    3. empty();//判断容器是否为空
    4. capacity();//容器的容量
    5. resize(int num);//更改大小,重新指定容器的长度为num,若容器变长,则以默认值填充新位置
    6. 如果容器变短,则末尾超出容器长度的元素被删除
    7. resize(int num, elem);//更改大小,重新指定容器的长度为num,若容器变长,则以elem填充新位置
    8. 如果容器变短,则末尾超出容器长度的元素被删除
    9. reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问

    示例: 

    1. void test02()
    2. {
    3. vector<int> v1(5, 100);//用5个100来构造自身,调用第三个拷贝构造
    4. vector<int>v3 (v1.begin(), v1.end());//区间构造,调用第二个拷贝构造
    5. vector<int> v4;
    6. v4 = v3;//使用重载的赋值运算符
    7. v3.assign(10,1);//给v3赋10个1
    8. Show(v3);
    9. v3.swap(v4);//交换v3,v4的值
    10. //大小,容量
    11. cout << "大小:" << v4.size() << "容量:" << v4.capacity() << endl;
    12. //容器是否为空
    13. vector<int>v5;
    14. if (v5.empty())
    15. {
    16. cout << "为空" << endl;
    17. }
    18. else {
    19. cout << "非空" << endl;
    20. }
    21. vector<int>v6(10, 30);
    22. v6.resize(20);//重置长度为20,过大后补0
    23. cout << "大小:" << v6.size() << "容量:" << v6.capacity() << endl;
    24. Show(v6);
    25. v6.resize(25, 5);//重置长度为25,过大后补5
    26. cout << "大小:" << v6.size() << "容量:" << v6.capacity() << endl;
    27. Show(v6);
    28. vector<int>v7;
    29. cout << "大小:" << v7.size() << "容量:" << v7.capacity() << endl;
    30. v7.reserve(100);//预留了100的容量空间
    31. cout << "大小:" << v7.size() << "容量:" << v7.capacity() << endl;
    32. }
    33. int main()
    34. {
    35. test02();
    36. return 0;
    37. }

    vector的插入和删除操作: 

    1. insert(const iterator pos, int count, ele);//迭代器指向pos位置插入count个元素ele
    2. push_back(elem);//尾部插入元素elem
    3. pop_back();//删除最后一个元素
    4. erase(const iterator start, const iterator end);//删除迭代器start到end间的元素
    5. erase(const iterator pos);//删除迭代器指向的元素
    6. clear();//删除容器中所有元素

    示例:

    1. void test02()
    2. {
    3. vector<int>v8;
    4. v8.push_back(1); //插入元素
    5. v8.push_back(2);
    6. v8.push_back(3);
    7. v8.pop_back();//尾删
    8. Show(v8);//1 2
    9. vector<int>v9;
    10. v9.insert(v9.begin(), 5, 2);//使用迭代器插入
    11. Show(v9);//2 2 2 2 2
    12. v9.insert(v9.begin()+2,2, 19);
    13. Show(v9);//2 2 19 19 2 2 2
    14. v9.insert(v9.end(), 2, 15);
    15. Show(v9);//2 2 19 19 2 2 2 15 15
    16. v9.erase(v9.begin(), v9.begin()+3);//删除了前三个元素
    17. Show(v9);
    18. v9.clear();//清空内容大小,容量不变
    19. cout << "大小:" << v9.size() << "容量:" << v9.capacity() << endl;
    20. }
    21. int main()
    22. {
    23. test02();
    24. return 0;
    25. }

     vector的数据存取操作:

    1. at(int index);//返回索引index所指的数据,如果index越界,跑出out of range异常
    2. operator[];//返回索引idx所指的数据,越界时,直接报错
    3. front();//返回容器中第一个数据元素
    4. back();//返回容器中最后一个元素

     示例:

    1. void test02()
    2. {
    3. vector<int>v8;
    4. v8.push_back(1); //插入元素
    5. v8.push_back(2);
    6. v8.push_back(3);
    7. cout << "头元素:" << v8.front() << " 尾元素:" << v8.back() << endl;
    8. //at越界会跑出异常,【】不会抛出异常
    9. cout << v8.at(1) << " " << v8[1] << endl;//取元素,下标从0开始
    10. v8.at(0) = 10;//赋值,更改
    11. v8[2] = 15;
    12. Show(v8);//10 2 15
    13. v8.pop_back();//尾删
    14. Show(v8);//10 2
    15. }
    16. int main()
    17. {
    18. test02();
    19. return 0;
    20. }

    vector常用的创建,循环插入,遍历:

    1. int main()
    2. {
    3. vector<int> x{1,2,3,4,5};//初始化x,赋值为1 2 3 4 5
    4. //vector x={1,2,3,4,5}等价
    5. //遍历
    6. for (auto num : x)
    7. {
    8. cout << num << " ";
    9. }
    10. cout << endl;
    11. x.clear();//清空
    12. // 插入
    13. for (int i = 0; i < 5; i++)//循环插入1 2 3 4 5
    14. {
    15. x.push_back(i+1);
    16. }
    17. //遍历
    18. for (vector<int>::iterator it = x.begin(); it != x.end(); it++)
    19. {
    20. cout << (*it) << " ";
    21. }
    22. cout << endl;
    23. //遍历
    24. for (auto it = x.begin(); it != x.end(); it++)
    25. {
    26. cout << (*it) << " ";
    27. }
    28. cout << endl;
    29. //遍历
    30. for (int j = 0; j < 5; j++)
    31. {
    32. cout << x[j] << " ";
    33. }
    34. cout << endl;
    35. vector<int>p(5); //容器开始就有5个元素,它们的默认初始值都为 0。
    36. for (int j = 0; j < p.size(); j++)
    37. {
    38. cout << p[j] << " ";
    39. }
    40. cout << endl;
    41. cout << p.front() << endl;
    42. cout << p.size() << endl;
    43. }

    vector利用swap收缩空间:

    在C++标准库容器vector的容量是不会自动的缩减的,也就是说删除元素操作,
    其引用、指针、迭代器也会继续有效。
    那么当在一个较大的vector中删除了大量的元素之后,
    其实际的size比较小,而其capacity比较大,如果对空间比较敏感,
    希望vector的容量能够缩小一些,这时可以使用下面的技巧来实现。

    1. void test03()
    2. {
    3. vector<int>v1;
    4. v1.reserve(500);
    5. v1.assign(5, 60);
    6. cout << "大小:" << v1.size() << "容量:" << v1.capacity() << endl;
    7. //使用拷贝构造函数创建匿名对象,调用匿名对象的swap函数
    8. //匿名对象在拷贝函数代码行结束后即被系统回收,实现了空间缩小
    9. //匿名对象在拷贝时只拷贝了他的有效空间
    10. vector<int>(v1).swap(v1);
    11. cout << "大小:" << v1.size() << "容量:" << v1.capacity() << endl;
    12. }
    13. int main()
    14. {
    15. test03();
    16. return 0;
    17. }

    我们会发现容量仅仅减少为之前的有效容量

    vector的遍历以及二维数组:

    二维数组其实就是一个数组包着许多一维数组构成,那么vector创建一维动态数组,如果创建二维也只需要"vector包着vector即可"

    1. void test04()
    2. {
    3. vector<int>v1(5, 10);
    4. vector<int>v2(4, 20);
    5. vector<int>v3(3, 30);
    6. //定义一个容器 存放v1 v2 v3
    7. //容器嵌套容器,二维数组
    8. vectorint>>v;
    9. v.push_back(v1);//插入
    10. v.push_back(v2);
    11. v.push_back(v3);
    12. //遍历 利用数组方式遍历
    13. for (int i = 0; i < v.size(); ++i)
    14. {
    15. for (int j = 0; j < v[i].size(); ++j)
    16. {
    17. cout << v[i][j] << " ";
    18. }
    19. cout << endl;
    20. }
    21. //利用迭代器的方式遍历
    22. vectorint>>::iterator it;
    23. for (it = v.begin(); it!= v.end(); ++it)
    24. {
    25. vector<int>::iterator mit;
    26. for (mit = (*it).begin(); mit != (*it).end(); ++mit)
    27. {
    28. cout << *mit << " ";
    29. }
    30. cout << endl;
    31. }
    32. }
    33. int main()
    34. {
    35. test04();
    36. return 0;
    37. }

    使用算法对vector数组进行排序:

    STL提供了大量的算法便于操作,这里我们仅用sort最简单的排序函数演示如何操作,后续将会吧所有常用的算法使用出一期文章。

    要了解:

    算法主要是由头文件 组成。
    是所有STL头文件中最大的一个,其中常用的功能涉及到比较,交换,查找,遍历,复制,修改,反转,排序,合并等…
    体积很小,只包括在几个序列容器上进行的简单运算的模板函数.
    定义了一些模板类,用以声明函数对象。

    1. void test05()
    2. {
    3. vector<int>v;
    4. v.push_back(5);//插入
    5. v.push_back(2);
    6. v.push_back(3);
    7. v.push_back(6);
    8. Show(v);
    9. //sort算法排序
    10. //包含头文件#include算法头文件
    11. sort(v.begin(), v.end());//默认从小到大排
    12. Show(v);
    13. sort(v.begin(), v.end(),greater<int>());//从大到小排 加个规则greater()
    14. Show(v);
    15. }
    16. int main()
    17. {
    18. test05();
    19. return 0;
    20. }

    vector存放自定义数据类型操作:

    在C++中对于类的使用是常态,因此对于vector操作自定义的数据类型要掌握。

    1. class Person
    2. {
    3. private:
    4. int num;
    5. string name;
    6. public:
    7. Person(){}
    8. Person(int num1, string name1) :num(num1), name(name1)
    9. {
    10. cout << "有参构造" << endl;
    11. }
    12. void Setnum(int num1) { this->num = num1; }
    13. void Setname(string name1) { this->name = name1; }
    14. int Getnum() {
    15. return num;
    16. }
    17. string Getname()
    18. {
    19. return name;
    20. }
    21. };
    22. void test06()
    23. {
    24. vector v;
    25. v.push_back(Person(12, "lucy"));
    26. v.push_back(Person(20, "Joe"));
    27. v.push_back(Person(36, "Tom"));
    28. vector::iterator it;
    29. for (it = v.begin(); it != v.end(); ++it)
    30. {
    31. cout << "年龄: " << (*it).Getnum() << "姓名:" << (*it).Getname();
    32. cout << endl;
    33. }
    34. 注意:对于自定义容器的排序,必须自己实现排序规则
    35. 或重载自定义数据类型的< 或>
    36. }
    37. int main()
    38. {
    39. test06();
    40. return 0;
    41. }

    看到这里相信对于vector的操作与使用就有了一定的了解,后续容器的很多使用也与vector近似,这一系列也会持续更新下去,感谢观看!

  • 相关阅读:
    Java 进阶IO流(二)
    【跟马少平老师学AI】-【神经网络是怎么实现的】(七-2)word2vec模型
    centos-7安装mysql-8
    使用eBPF加速阿里云服务网格ASM
    docker系列(6) - docker数据卷
    Electron学习——解决npm install electron --save-dev出错/缓慢的问题
    Python爬虫爬取豆瓣电影短评(爬虫入门,Scrapy框架,Xpath解析网站,jieba分词)
    恶劣条件下GNSS定位的鲁棒统计
    Python实现模块热加载
    Rakis: 免费基于 P2P 的去中心化的大模型
  • 原文地址:https://blog.csdn.net/weixin_51609435/article/details/126206413