C++ STL


title: C++ STL
tags: [c++,stl]
categories:

  • c++
  • stl
    date: 2021-06-15 15:00:20

C++ STL

c语言中文网的STL教程

stl 是一个将算法与底层抽象实现相分离的库,通过它我们可以很轻松地,高效地实现很多复杂的算法。但是学习stl不仅仅要学习其使用方式,更重要的是学习其底层实现,若是掌握了其底层实现逻辑,则不仅拥有了扎实的数据结构的基本功,更是在c++修仙之路上修炼了一本武林秘籍。

c++ stl 的头文件有:

<iterator>      
<functional>    
<vector>    
<deque>     
<list>      
<queue>     
<stack>     
<set>       
<map>       
<algorithm>     
<numeric>       
<memory>        
<utility>      

stl提供三种标准容器,分别为序列容器,排序容器,哈希容器。后两种也称为关联容器。

容器种类功能
序列容器主要包括vector,list,deque。元素在容器中的位置和元素的值没有关系,容器是非排序的。将元素插入时,指定在什么位置,元素就会在什么位置
排序容器包含set,multiset,map,multimap.元素默认是从大到小排好的,插入元素时元素会自动插入到适当的位置。关联容器在查找时有着很好的性能。
哈希容器unordered_set,unordered_map,unordered_multiset,unordered_multimap。和排序容器不同,哈希容器中元素的位置是未排序的,元素的位置有哈希函数确定。

迭代器

stl标准库为每一种容器定义了一种迭代器,不同的容器迭代器不同,其功能强弱也不同。

  1. 前向迭代器(forward iterator)
    假设p是一个前向迭代器,则p支持p++,++p,*p操作,可以用相互赋值,可以使用==与!=进行比较。
  2. 双向迭代器(bidirectional iterator)
    相比于前向迭代器,可以进行–p和p–操作。
  3. 随机访问迭代器(random acccess iterator)
    除了双向迭代器的所有操作还支持以下操作:
    1. p+=i
    2. p-=i
    3. p+i
    4. p-i
    5. p[i]

不同容器的迭代器

容器迭代器
array随机
vector随机
deque随机
list双向
set/multiset双向
map/multimap双向
forward_list前向
unordered_set/unordered_multiset前向
unordered_map/unordered_multimap前向
stack不支持
queue不支持

迭代器的定义方式

迭代器类型具体格式
正向迭代器容器类名::iterator 迭代器名
常量正向迭代器容器类名::const_iterator 迭代器名;
反向迭代器容器类名::reverse_iterator 迭代器名
常量反向迭代器容器类名::const_reverse_iterator 迭代器名

序列式容器

array
#include <iostream>
#include <array>
using namespace std;
int main()
{
    array<int, 4> myarray{};
    //初始化 myarray 容器为 {0,1,2,3}
    for (int i = 0; i < myarray.size(); i++)
    {
        myarray.at(i) = i;
    }
    //使用 get() 重载函数输出指定位置元素
    cout << get<3>(myarray) << endl;
    cout << myarray[3] << endl;

    //正向迭代器
    array<int, 4>::iterator it;
    if (!myarray.empty())
        for (it = myarray.begin(); it <= myarray.end() - 1; it++)
            cout << *it << " ";
    cout << endl;
    //反向迭代器
    array<int, 4>::reverse_iterator it_r;
    if (!myarray.empty())
        for (it_r = myarray.rend() - 1; it_r >= myarray.rbegin(); it_r--)
            cout << *it_r << " ";
    cout << endl;

    //随机访问
    int index = 1;
    cout << myarray[index] << " " << myarray[index + 1] << endl;
    //返回容器中第一个元素的直接引用,该函数不适用于空的 array 容器。
    cout << myarray.front() << endl;
    //返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器。
    cout << myarray.back() << endl;

    //返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能。
    int *p = myarray.data();
    cout << *p << endl;

    //将值赋值给容器中的每个元素。
    myarray.fill(9);
    if (!myarray.empty())
        for (it = myarray.begin(); it <= myarray.end() - 1; it++)
            cout << *it << " ";
    cout << endl;

    //交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型。
    array<int, 10> array1;
    array1.fill(1);
    array<int, 10> array2;
    array2.fill(2);
    array<int, 10>::iterator it_10;
    if (!array1.empty())
        for (it_10 = array1.begin(); it_10 <= array1.end() - 1; it_10++)
            cout << *it_10 << " ";
    cout << endl;
    if (!array2.empty())
        for (it_10 = array2.begin(); it_10 <= array2.end() - 1; it_10++)
            cout << *it_10 << " ";
    cout << endl;
    //交换
    array1.swap(array2);
    if (!array1.empty())
        for (it_10 = array1.begin(); it_10 <= array1.end() - 1; it_10++)
            cout << *it_10 << " ";
    cout << endl;
    if (!array2.empty())
        for (it_10 = array2.begin(); it_10 <= array2.end() - 1; it_10++)
            cout << *it_10 << " ";
    cout << endl;
}

vector

vector 容器是 STL 中最常用的容器之一,它和 array 容器非常类似,都可以看做是对 C++ 普通数组的“升级版”。不同之处在于,array 实现的是静态数组(容量固定的数组),而 vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会动态调整所占用的内存空间,整个过程无需人工干预。vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)。

还需注意的是,如果调用 reserve() 来增加容器容量,之前创建好的任何迭代器(例如开始迭代器和结束迭代器)都可能会失效,这是因为,为了增加容器的容量,vector 容器的元素可能已经被复制或移到了新的内存地址。所以后续再使用这些迭代器时,最好重新生成一下。

#include <iostream>
#include <array>
#include <vector>
using namespace std;
int main()
{
    vector<double> v0{1.0, 2.0, 3.0};
    //获取当前容量
    cout << v0.capacity() << endl;
    //申请100个元素空间,增加容器的容量。
    //如果vector的容量在执行此语句之前,已经大于或等于100个元素,那么这条语句什么也不做;
    v0.reserve(100);
    cout << v0.capacity() << endl;
    //减少内存到当前元素实际使用的的大小
    v0.shrink_to_fit();
    cout << v0.capacity() << endl;
    //实际使用元素个数
    cout << v0.size() << endl;

    //迭代器
    vector<double>::iterator it1;
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //反向迭代器在array中示范过,不再赘述。

    //指定元素个数,其默认值都为0
    vector<double> v1(100);
    if (!v0.empty())
        for (it1 = v1.begin(); it1 <= v1.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //指定默认值为10
    vector<double> v2(100, 10);
    if (!v0.empty())
        for (it1 = v2.begin(); it1 <= v2.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //使用已经存在的vector创建新的vector
    vector<double> v3(v2);
    if (!v0.empty())
        for (it1 = v3.begin(); it1 <= v3.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //也可以只取一部分
    vector<double> v4(v2.data(), v2.data() + 1);
    if (!v0.empty())
        for (it1 = v4.begin(); it1 <= v4.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;

    //使用下标访问
    cout << v0[0] << endl;
    cout << v0.at(0) << endl;

    //返回第一个元素的引用
    cout << v0.front() << endl;
    //返回最后一个元素的引用
    cout << v0.back() << endl;

    //替换原有的元素
    v0.assign(v1.data(), v1.data() + 5);
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;

    //在尾部插入元素
    v0.push_back(1);
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //从尾部移除元素
    v0.pop_back();
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;

    //在指定位置插入一个或者多个元素
    it1 = v0.begin();
    //在指定位置插入元素,返回指向该元素的迭代器
    cout << *it1 << endl;
    it1 = v0.insert(it1, 1);
    cout << *it1 << endl;
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //在指定位置前插入多个元素 插入两个4
    it1 = v0.begin();
    v0.insert(it1, 2, 4);
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //在指定为之前插入指定元素,同assign
    //事实证明,是先将源内存区域的数据复制下来再插入进入。
    it1 = v0.begin();
    v0.insert(it1, v0.data(), v0.data() + 7);
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;

    //删除指定的元素
    it1 = v0.begin();
    v0.erase(it1);
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //依次删除所有元素 注意这里,删除迭代器指向的元素之后,后面的元素会自动前移,所以不需要更新迭代器。
    it1 = v0.begin();
    while (it1 != v0.end())
        v0.erase(it1);
    if (v0.empty())
        cout << "空了" << endl;
    //删除指定区间的元素 前闭后开
    for (int i = 0; i <= 10; i++)
        v0.push_back(i);
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    it1 = v0.begin();
    v0.erase(it1, it1 + 2);
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;

    //清理元素 元素没有了,但是容量还在 事实证明擦除的内存被写入了-4.8367e-26
    int re_size = v0.size();
    it1 = v0.begin();
    for (int i = 0; i <= re_size; i++)
        cout << *(it1 + i) << endl;
    cout << v0.capacity() << endl;
    cout << v0.size() << endl;
    v0.clear();
    cout << v0.capacity() << endl;
    cout << v0.size() << endl;
    if (v0.empty())
        cout << "空了" << endl;
    v0.reserve(100);
    it1 = v0.begin();
    for (int i = 0; i <= re_size; i++)
        cout << *(it1 + i) << endl;

    //交换元素
    v0.clear();
    v1.clear();
    v0.push_back(0);
    v1.push_back(1);
    it1 = v0.begin();
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    it1 = v1.begin();
    if (!v1.empty())
        for (it1 = v1.begin(); it1 <= v1.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    v0.swap(v1);
    it1 = v0.begin();
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    it1 = v1.begin();
    if (!v1.empty())
        for (it1 = v1.begin(); it1 <= v1.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;

    //再指定位置直接生成一个元素 emplace 一次只可以插入一个元素,效率比insert高
    it1 = v0.begin();
    v0.emplace(it1, 2);
    it1 = v0.begin();
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
    //相当于 push_back
    v0.emplace_back(3);
    it1 = v0.begin();
    if (!v0.empty())
        for (it1 = v0.begin(); it1 <= v0.end() - 1; it1++)
            cout << *it1 << " ";
    cout << endl;
}
deque 双队列容器

和 vector 相比,额外增加了实现在容器头部添加和删除元素的成员函数,同时删除了 capacity()、reserve() 和 data() 成员函数。

#include <iostream>
#include <array>
#include <vector>
#include <deque>
using namespace std;
int main()
{
    deque<int> dq{0, 1, 2, 3, 4, 5};
    deque<int>::iterator it;
    it = dq.begin();
    if (!dq.empty())
        for (it = dq.begin(); it <= dq.end() - 1; it++)
            cout << *it;
    cout << endl;
    //相vector额外怎增加了从头删除与加入元素的额方法。
    dq.push_front(1);
    it = dq.begin();
    if (!dq.empty())
        for (it = dq.begin(); it <= dq.end() - 1; it++)
            cout << *it;
    cout << endl;
    dq.pop_front();
    it = dq.begin();
    if (!dq.empty())
        for (it = dq.begin(); it <= dq.end() - 1; it++)
            cout << *it;
    cout << endl;
}
list 双链表容器
#include <iostream>
#include <array>
#include <vector>
#include <deque>
#include <list>
using namespace std;

bool demo(int a, int b)
{
    return (a <= b);
}
int main()
{
    list<int> l(10, 0);
    list<int>::iterator it;
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it;
    cout << endl;

    //好多和vector重复的函数就不再赘述。
    //由于是双向链表,故前后都可以插入删除,和deque一样。

    //删除所有指定大小的元素
    l.remove(0);
    if (l.empty())
        cout << "你把0都删除了,我空了" << endl;
    list<int> l2(5, 2);

    //将一个 list 容器中的元素插入到另一个容器的指定位置。
    //当begin==end 时,为空,所以下面也可以换成 l.end()
    l.splice(l.begin(), l2);
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it;
    cout << endl;

    l = {1, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 9, 10, 10, 1};
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it;
    cout << endl;
    //删除容器中相邻的重复元素,只保留一个。 可见只比较相邻的,若要实现真正的unique需要结合sort()进行排序
    l.unique();
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it;
    cout << endl;
    //使用一个二元谓语函数作为参数删除 此函数的作用是自定义前后两个元素的关系。
    //此例时若前一个元素值比后一个小则删除掉前一个
    l.unique(demo);
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it;
    cout << endl;
    //排序
    l.sort();
    l.unique();
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it;
    cout << endl;
    //合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。 事实证明合并前的list必须都排好序。
    l2 = {1, 4, 2, 31, 54, 234, 2, 523, 42, 42, 10};
    l.merge(l2);
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it << " ";
    cout << endl;
    l.sort();
    l2.sort();
    l.merge(l2);
    l.unique();
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it << " ";
    cout << endl;

    //反转容器内元素的顺序
    l.reverse();
    it = l.begin();
    if (!l.empty())
        for (it = l.begin(); it != l.end(); it++)
            cout << *it << " ";
    cout << endl;

    //条件删除 remove_if
    //现在我对泛型不太了解,明日有精力了再写。
}
forward_list

其实就是单链表,相比于双向链表list,forward_list 更加有效率,但是不能进行逆向遍历的操作。当一个操作list与forward_list都可以做的时候,应该优先选用forward_list.

关联式容器

关联式容器再存储元素的时候还会给每一个元素配备一个键,整体以键值相配对的方式进行存储,可以通过键值直接索引到元素无需遍历整个容器,再存储元素的时候会默认按照各个元素的大小做升序排列。关联式容器在更删改查的效率更高。关联式容器在底层使用红黑树进行实现。

关联式容器包括:

map
multimap
set
multiset
容器特点
map元素的键是唯一的,根据各元素键的大小进行升序排序
set元素的键是唯一的,根据各元素键的大小进行升序排序
multimap和map的区别在于键可以重复
multiset和set的区别在于键可以重复
map

map容器存储的都是pair对象。

pair<const K, T>

键是不可以修改的。值是可以修改的。

#include <map>
#include <iostream>
using namespace std;
int main()
{
    //声明
    map<int, string> mymap{{3, "三"}, {4, "四"}};
    //插入元素 可以像数组一样进行 [] 运算
    mymap[1] = "一";
    mymap[2] = "二";

    //迭代器
    map<int, string>::iterator it;
    //遍历
    it = mymap.begin();
    if (!mymap.empty())
        for (it = mymap.begin(); it != mymap.end(); it++)
            cout << it->first << " " << it->second << endl;
    //试着插入一个重复的键值 事实证明重复插入就取代了之前的
    mymap[1] = "新一";
    it = mymap.begin();
    if (!mymap.empty())
        for (it = mymap.begin(); it != mymap.end(); it++)
            cout << it->first << " " << it->second << endl;

    //下面介绍相比与vector特有的成员函数。
    //根据键查找元素,返回一个指向结果元素的迭代器。
    it = mymap.find(1);
    cout << it->first << " " << it->second << endl;
    //返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器.
    it = mymap.lower_bound(2);
    cout << it->first << " " << it->second << endl;
    //返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器
    it = mymap.upper_bound(2);
    cout << it->first << " " << it->second << endl;
    //返回一个范围 两个it组成的一个pair,该范围中包含的键为 key 的键值对.
    //因为map中元素的唯一性,所以该范围最多包含一个键值对。前闭后开。
    pair<map<int, string>::iterator, map<int, string>::iterator> pair;
    pair = mymap.equal_range(2);
    cout << "范围low: " << pair.first->first << " " << pair.first->second << endl;
    cout << "范围high: " << pair.second->first << " " << pair.second->second << endl;

    //可以像数组一样进行 [] 运算 若不存在的话就插入一个key,其value为空。
    cout << mymap[8] << endl;
    cout << mymap[1] << endl;
    //at找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。
    cout << mymap.at(2) << endl;
    if (!mymap.at(8).empty())
        cout << mymap.at(8) << endl;
    //else cout<<"该key没有对应"<<endl;

    //插入 若插入的元素已经存在的话则会插入失败,所以需要删除在插入或者修改。
    mymap.insert({5, "五"});
    mymap.insert({5, "肆虐五"});
    it = mymap.begin();
    if (!mymap.empty())
        for (it = mymap.begin(); it != mymap.end(); it++)
            cout << it->first << " " << it->second << endl;
    //删除
    mymap.erase(8);
    it = mymap.begin();
    if (!mymap.empty())
        for (it = mymap.begin(); it != mymap.end(); it++)
            cout << it->first << " " << it->second << endl;

    //交换两个容器的内容 swap() 不在赘述

    //统计key出现的次数
    cout << mymap.count(1) << endl;
}
multimap

multimap 比 map 除了可以拥有重复的额值之外,没有什么不同的。略过。

set

和 map、multimap 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。

其余和map没啥不同,set就是集合,所以也不可以重复。

multiset

multiset 比 set 除了可以拥有重复的额值之外,没有什么不同的。略过。

无序关联式容器

无序关联式容器,又称哈希容器。和关联式容器一样,此类容器存储的也是键值对元素;不同之处在于,关联式容器默认情况下会对存储的元素做升序排序,而无序关联式容器不会。和其它类容器相比,无序关联式容器擅长通过指定键查找对应的值,而遍历容器中存储元素的效率不如关联式容器。

关联式容器的底层实现采用的树存储结构,更确切的说是红黑树结构;
无序容器的底层实现采用的是哈希表的存储结构。

无序容器内部存储的键值对是无序的,各键值对的存储位置取决于该键值对中的键,
和关联式容器相比,无序容器擅长通过指定键查找对应的值(平均时间复杂度为 O(1));但对于使用迭代器遍历容器中存储的元素,无序容器的执行效率则不如关联式容器。

包含有 4 个具体容器,分别为 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset。

c++ 的常用算法

p 除了可以拥有重复的额值之外,没有什么不同的。略过。

set

和 map、multimap 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。

其余和map没啥不同,set就是集合,所以也不可以重复。

multiset

multiset 比 set 除了可以拥有重复的额值之外,没有什么不同的。略过。

无序关联式容器

无序关联式容器,又称哈希容器。和关联式容器一样,此类容器存储的也是键值对元素;不同之处在于,关联式容器默认情况下会对存储的元素做升序排序,而无序关联式容器不会。和其它类容器相比,无序关联式容器擅长通过指定键查找对应的值,而遍历容器中存储元素的效率不如关联式容器。

关联式容器的底层实现采用的树存储结构,更确切的说是红黑树结构;
无序容器的底层实现采用的是哈希表的存储结构。

无序容器内部存储的键值对是无序的,各键值对的存储位置取决于该键值对中的键,
和关联式容器相比,无序容器擅长通过指定键查找对应的值(平均时间复杂度为 O(1));但对于使用迭代器遍历容器中存储的元素,无序容器的执行效率则不如关联式容器。

包含有 4 个具体容器,分别为 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset。

c++ 的常用算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值