C++STL库学习笔记(二)——容器

2023-05-16

标准模板库包含了序列容器(sequence containers)与关系容器(associative containers)。

数据容器描述

序列容器 - 有序集

vector动态数组,兼容C语言数组。vector可以如同数组一样的访问方式,例如使用下标(operator[])运算符,并记得自己的长度信息(size),您也可以使用对象的方式来访问vector(push_back、pop_back)。使用vector可以轻易地定义多维可调整型数组(std::vector<std::vector<...> >)。要使用vector,必须含入vector头文件。vector可在O(1)内完成在末尾插入 / 移除元素,但在vector中间或开头插入/移除元素,则需要消耗O(n)时间。
listlist容器是一个有序(Ordered)的数据结构(循序容器),每个元素中存储着上一个元素和下一个元素的地址(指针),因此是一个双向链接的链表。与vector相比,其元素的访问速度较慢,而在已知元素位置的情况下,插入和删除速度较快。STL容器中唯一支持事务语义。
forward_list
(单向链表)
list的单链表版,去掉了一些操作。
deque
(双端队列)
可看做为能在常量时间内完成向开头或结尾插入或删除元素的vector,但是修改之后,其迭代器的有效性就无法得到保障。
array只能在初始化时指定大小的数组,可视为内置数组的封装。

关联容器 - 无序集

set不重复元素的集合。
multiset跟set具有相同功能,但允许重复的元素。
map关联数组,每个元素含有两个数据项,map将一个数据项映射到另一个数据项中。
multimap跟map具有相同功能,但允许重复的键值。
unordered_set
unordered_multiset
unordered_map
unordered_multimap
分别类似于集合、多重集合、映射、多重映射,但使用哈希表实现。它的键(Keys)没有排序(operator<),相反必须存在一个从键类型到size_t的哈希函数、且要求键之间可以判等(operator==)。自C++11起进入语言标准。
其他类型的容器
bitset存储系列位类似的固定大小的布尔向量。实现按位运算,没有迭代器,不是序列。可视为std::array<bool, N>。若需要改变序列长度,可用std::vector<bool>。
valarray数值类型的std::vector。牺牲泛型能力而专为数值计算做了优化,例如在数组上的sin操作可对数组内所有数值取正弦。有些实现会对std::valarray应用向量指令等优化手段。
一个观点是里面全是数值类型的valarray才是数学意义上的向量,而可以泛型的vector更该叫array——编程语言中的数组。

1.vector

     1)介绍

  • vector是表示可变大小数组的序列容器。
  • 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  • 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  • vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  • 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  • 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。 

    2)用法 

        

1. 头文件

#include<vector>

2. vector声明及初始化

vector<int> vec;        //声明一个int型向量
vector<int> vec(5);     //声明一个初始大小为5的int向量
vector<int> vec(10, 1); //声明一个初始大小为10且值都是1的向量
vector<int> vec(tmp);   //声明并用tmp向量初始化vec向量
vector<int> tmp(vec.begin(), vec.begin() + 3);  //用向量vec的第0个到第2个值初始化tmp
int arr[5] = {1, 2, 3, 4, 5};   
vector<int> vec(arr, arr + 5);      //将arr数组的元素用于初始化vec向量
//说明:当然不包括arr[4]元素,末尾指针都是指结束元素的下一个元素,
//这个主要是为了和vec.end()指针统一。
vector<int> vec(&arr[1], &arr[4]); //将arr[1]~arr[4]范围内的元素作为vec的初始值

vector<vector<int> >;  //注意空格。这里相当于二维数组int a[n][n];
vector<int> v5 = { 1,2,3,4,5 }; //列表初始化,注意使用的是花括号
vector<string> v6 = { "hi","my","name","is","lee" };
vector<int> v7(5, -1); //初始化为-1,-1,-1,-1,-1。第一个参数是数目,第二个参数是要初始化的值
vector<string> v8(3, "hi");
vector<int> v9(10); //默认初始化为0
vector<int> v10(4); //默认初始化为空字符串

3. vector基本操作

(1). 容量

  • 向量大小: vec.size();
  • 向量最大容量: vec.max_size();
  • 更改向量大小: vec.resize();
  • 向量真实大小: vec.capacity();
  • 向量判空: vec.empty();
  • 减少向量大小到满足元素所占存储空间的大小: vec.shrink_to_fit(); //shrink_to_fit

(2). 修改

  • 多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值
  • 末尾添加元素: vec.push_back();
  • 末尾删除元素: vec.pop_back();
  • 任意位置插入元素: vec.insert();
  • 任意位置删除元素: vec.erase();
  • 交换两个向量的元素: vec.swap();
  • 清空向量元素: vec.clear();

(3)迭代器

  • 开始指针:vec.begin();
  • 末尾指针:vec.end(); //指向最后一个元素的下一个位置
  • 指向常量的开始指针: vec.cbegin(); //意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。
  • 指向常量的末尾指针: vec.cend();

(4)元素的访问

  • 下标访问: vec[1]; //并不会检查是否越界
  • at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常
  • 访问第一个元素: vec.front();
  • 访问最后一个元素: vec.back();
  • 返回一个指针: int* p = vec.data(); //可行的原因在于vector在内存中就是一个连续存储的数组,所以可以返回一个指针指向这个数组。这是是C++11的特性。

(4)算法

  • 遍历元素
vector<int>::iterator it;
for (it = vec.begin(); it != vec.end(); it++)
    cout << *it << endl;
//或者
for (size_t i = 0; i < vec.size(); i++) {
    cout << vec.at(i) << endl;
}
  • 元素翻转
#include <algorithm>
reverse(vec.begin(), vec.end());
  • 元素排序
#include <algorithm>
sort(vec.begin(), vec.end()); //采用的是从小到大的排序
//如果想从大到小排序,可以采用上面反转函数,也可以采用下面方法:
bool Comp(const int& a, const int& b) {
    return a > b;
}
sort(vec.begin(), vec.end(), Comp);

以上内容摘自 https://www.cnblogs.com/zhonghuasong/p/5975979.html

2.list 

    1)介绍

  • list 被定义在 <list> 标头档中。一如其他STL元件,list属于std名称空间。
  • list 内部以数据结构的双向连结串列实做,内部元素 内存各处,互相以link串接起来,每个元素都只知道其前一个元素以及下一个元素的位置。故要走访整个list,必须从第一个元素开始逐个往下寻访,不支持随机存取(Random Access)。 list 的强项是高效的插入以及删除,于list插入或删除时只需要改动元素的link字段,不需要搬动元素,代价相对便宜。
  • list 在经常需要于集合内部任意位置(即除了头尾以外的其他位置) 频繁增删元素的工作上表现优秀。若仅需要于集合尾端增删元素,那应该优先考虑vector容器,若仅于头尾二端增删元素,那应该优先考虑deque容器。

    2 )用法

1. 头文件

#include <list>

2. list声明及初始化

list_t  List;  //定义一个空的链表

list_t  List(count); //建一个含count个默认值是0的元素的链表

list_t  List(count, info); //建一个含count个默认值是info的元素的链表

list_t  List(List2);   //建一个的copy链表

list_t  List(List2.begin(),List2.end());   //含区间的元素[First,Last]

3. list基本操作

  • 迭代 (Iterator)
    • list.begin() 回传指向第一个元素的 Iterator。
    • list.end() 回传指向最末元素的下一个位置的 Iterator。
    • list.rbegin() 回传指向最末个元素的反向 Iterator。
    • list.rend() 回传指向第一个元素的前一个位置的反向 Iterator。
  • Capacity/Size:
    • list.empty() 若list内部为空,则回传true值。
    • list.size() 回传list内实际的元素个数。
    • list.resize() 重新分派list的长度。
  • 存取元素的方法
    • list.front() 存取第一个元素。
    • list.back() 存取最末个元素。
  • Modify methods
    • list.push_front() 增加一个新的元素在 list 的前端。
    • list.pop_front() 删除 list 的第一个元素。
    • list.push_back() 增加一个新的元素在 list 的尾端。
    • list.pop_back() 删除 list 的最末个元素。
    • list.insert() - 插入一个或多个元素至 list内的任意位置。
    • list.erase() - 删除 list中一个或多个元素。
    • list.clear() - 清空所有元素。
  • 重新配置/重设长度
    • list.reserve() - 如有必要,可改变 list的容量大小(配置更多的内存)。
    • list.resize() - 改变 list目前持有的元素个数。

(以上内容来自于维基百科)

 3.set

    1)介绍

set跟vector差不多,它跟vector的唯一区别就是,set里面的元素是有序的且唯一的,只要你往set里添加元素,它就会自动排序,而且,如果你添加的元素set里面本来就存在,那么这次添加操作就不执行。

关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。

    2)用法

1. 头文件

#include <set>

2. set声明及初始化

/*
set和multiset的内部结构通常是由平衡二叉树来实现的。当元素放入容器中时,会按照一定的排序法则自动排序,默认是按照less<>排序规则来排序。这种自动排序的特性加速了元素查找的过程,但是也带来了一个问题,不可以直接修改set或multiset容器中的元素值,因为这样就可能违反了元素自动排序的规则。如果你希望修改一个元素的值,必须先删除原有的元素,再插入新的元素。
一个集合(set)是一个容器,它其中的所包含的元素的值是唯一的。集合多集的区别是:set支持唯一的键值,set中的值都是特定的,而且只出现一次;而multiset中可以出现副本键,同一值可以出现多次。
*/
set<int> seone;
multiset<int> musone;

3. set基本操作

  • begin()    返回set容器的第一个元素的地址

    end()      返回set容器的最后一个元素地址

    clear()    删除set容器中的所有的元素

    empty()     判断set容器是否为空

    max_size()   返回set容器可能包含的元素最大个数

    size()      返回当前set容器中的元素个数

    erase(it) 删除迭代器指针it处元素

    insert(a) 插入某个元素 

以上内容来自于https://www.cnblogs.com/BeyondAnyTime/archive/2012/08/13/2636375.html

4.map 

    1)简介

map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。对于迭代器来说,可以修改实值,而不能修改key。 

    2)用法

 

1. 头文件

#include <map>

2. map声明及初始化

map<string , int >mapstring;         
map<int ,string >mapint;
map<sring, char>mapstring;         
map< char ,string>mapchar;
map<char ,int>mapchar;            
map<int ,char >mapint;

3. map基本操作

  • 1. map添加数据;

       map<int ,string> maplive;  
       1.maplive.insert(pair<int,string>(102,"aclive"));
       2.maplive.insert(map<int,string>::value_type(321,"hai"));
       3, maplive[112]="April";//map中最简单最常用的插入添加!


    2.map中元素的查找:

       find()函数返回一个迭代器指向键值为key的元素,如果没找到就返回指向map尾部的迭代器。        

       map<int ,string >::iterator l_it;; 
       l_it=maplive.find(112);
       if(l_it==maplive.end())
                    cout<<"we do not find 112"<<endl;
       else cout<<"wo find 112"<<endl;


    3.map中元素的删除:
       如果删除112;
       map<int ,string >::iterator l_it;;
       l_it=maplive.find(112);
       if(l_it==maplive.end())
            cout<<"we do not find 112"<<endl;
       else  maplive.erase(l_it);  //delete 112;

      4.其他

      begin()          返回指向map头部的迭代器
      count()          返回指定元素出现的次数
      empty()          如果map为空则返回true
      end()            返回指向map末尾的迭代器
      equal_range()    返回特殊条目的迭代器对
      erase()          删除一个元素
      find()           查找一个元素
      get_allocator()  返回map的配置器
      insert()         插入元素
      key_comp()       返回比较元素key的函数
      lower_bound()    返回键值>=给定元素的第一个位置
      max_size()       返回可以容纳的最大元素个数
      rbegin()         返回一个指向map尾部的逆向迭代器
      rend()           返回一个指向map头部的逆向迭代器
      size()           返回map中元素的个数
      swap()            交换两个map
      upper_bound()     返回键值>给定元素的第一个位置
      value_comp()      返回比较元素value的函数

以上内容来自https://www.cnblogs.com/qigaohua/p/5803629.html

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++STL库学习笔记(二)——容器 的相关文章

随机推荐