标准模板库(STL)

标准模板库(STL):标准库提供了一些可以直接使用的功能

        指的是C++标准库中使用模板实现的内容(模板类和模板函数)

        其中最主要的组件(容器/迭代器/算法)

容器:
        可以存放其他对象的对象,就是容器,标准库中最典型的容器就是使用类模板实现的常用的数据结构

        根据数据结构本身的特点,可以把容器大概分为两类:

                顺序容器:

                        如:数组、双端数组、栈、队列、链表 ......

                关联容器:

                        如:集合(set),映射(map)


迭代器:
        使用面向对象的技术封装起来的"高级指针",具有与指针类似的行为(表示一个位置,同时可以访问容器中这个位置的数据)

        在标准库中,
大部分的容器都实现了迭代器(用来访问容器中的元素),栈和队列没有

算法:

        使用函数模板实现的常用的操作容器的算法(功能),如:

                排序,交换,查找,遍历,合并......

        很多算法都是用来操作容器中的元素的(所有元素/使用迭代器表示的一个区间)

以方法为主,记住常用核心的接口和功能,其他的可以查看帮助文档——cppreference

01 迭代器(iterator)

迭代器用于一个对象集合上的元素进行遍历动作,对象集合可能是容器,也可能是容器的一部分
 

迭代器(iterator):是用面向对象的技术封装起来的"高级指针",表示集合中某一个元素的位置

        目的是将遍历容器的方法和容器内部的实现分开,对于用户来说
        不需要知道集合内部的结构,就可以使用一种通用的方法去遍历容器内部的所有元素(迭代器模式)

        迭代器是一个对象,相当于容器和操作容器的算法之间的中介(表示一个位置/范围)
        迭代器可以指向容器中的某一个元素(位置),通过迭代器我们可以操作这个元素,所以迭代器和指针一样是类型

迭代器按照定义分为四类:

1. 正向迭代器

        定义方式:

                容器类名<类型参数>::iterator  迭代器名称; // 实例化一个迭代器

array<int, 10> arr;
arrar<int, 10>::iterator it = arr.begin(); // 定义并且初始化了一个迭代器对象
当++操作的时候,指向的位置是往后移动的
可以利用迭代器去读取和修改容器中的元素

容器类一般向外提供获取迭代器的接口:
    begin() 获取容器的正向迭代器的首部(第一个元素)
    end()   获取容器的正向迭代器的尾部(最后一个元素的后面)  
2. 常量正向迭代器(const)

        定义方式:

               容器类名<类型参数>::const_iterator  迭代器名称;  // 实例化一个迭代器

array<int, 10> arr;
// 定义并且初始化了一个迭代器对象
arrar<int, 10>::const_iterator it = arr.cbegin();
当++操作的时候,指向的位置是往后移动的
只能利用迭代器去读取元素,不能修改容器中元素的值

容器类一般向外提供获取迭代器的接口:
    cbegin() 获取容器的正向迭代器的首部(第一个元素)
    cend()   获取容器的正向迭代器的尾部(最后一个元素的后面)
3. 反向迭代器(reverse)

        定义方式:

                容器类名<类型参数>::reverse_iterator  迭代器名称; // 实例化一个迭代器

array<int, 10> arr;
// 定义并且初始化了一个迭代器对象
arrar<int, 10>::reverse_iterator it = arr.rbegin();
当++操作的时候,指向的位置是往前移动的
可以利用迭代器去读取和修改容器中的元素

容器类一般向外提供获取迭代器的接口:
    rbegin() 获取容器的反向迭代器的首部(最后一个元素)
    rend()   获取容器的反向迭代器的尾部(第一个元素的前面)
4. 常量反向迭代器

        定义方式:

                容器类名<类型参数>::const_reverse_iterator  迭代器名称; // 实例化一个迭代器

array<int, 10> arr;
// 定义并且初始化了一个迭代器对象
arrar<int, 10>::const_reverse_iterator it = arr.crbegin();
当++操作的时候,指向的位置是往前移动的
只能利用迭代器去读取容器中的元素

容器类一般向外提供获取迭代器的接口:
    crbegin() 获取容器的正向迭代器的首部(第一个元素)
    crend()   获取容器的正向迭代器的尾部(最后一个元素的后面)

迭代器都是可以进行++操作的,反向迭代器和正向迭代器的区别:

        当正向迭代器进行++操作时,迭代器会指向容器的下一个元素位置
        当反向迭代器进行++操作时,迭代器会指向容器的上一个元素位置


迭代器的 ++it 相对于 it++ 的操作,程序的效率会更高
        在实现迭代器对象的后置++时,会产生一个局部的临时对象(参考运算符重载)

通过迭代器可以读取和修改它指向的元素

        *迭代器对象名称

                就表示迭代器指向的元素
        cout << *it <<endl; // 读取

        *it = 1024; // 修改

注意:

        (1) 大部分的容器都实现了迭代器,但是容器适配器(stack、queue、priority_queue)没有迭代器,但是容器适配器中有些成员函数可以用来操作容器指定元素
                适配器模式:可以根据相似的类,进行某些功能的删除或者修改,适配得到一个新的类(新类型的内部是由老类型实现的),这个就是适配器模式

                        如:stack是一个先进后出的栈,stack内部就可以使用vector实现,就成为了一个栈(符合先进后出的特定)
 

        (2) 不同的迭代器功能强弱不一样,迭代器的功能强弱,决定了该容器是否支持STL中的某些算法!!!

                如:排序算法需要通过随机访问迭代器来访问容器中的元素,因此有些容器不支持排序算法
 

常用的迭代器按照功能强弱:
        迭代器都拥有基础的功能:==  !=  ++  * ......
       
1. 单向迭代器(只能++)
            it++ / ++it

        2. 双向迭代器(能++ / 能--)
            it++ / ++it / it-- / --it

        3. 随机访问迭代器(能自增自减,还能it = it+n)
            类似于指针的特性
            能够
            it++ / ++it / it-- / --it
            也能:一次性移动i个位置
            it = it + i
            it = it - i

            此外随机访问迭代器 it1 和 it2 能够使用<、>、>=、<=、==等进行直接比较

            如果 it1 < it2成立,说明 it1 经过 i 次自增能够和 it2 表示同一个位置

常用的容器支持的迭代器:

        array           静态数组          随机访问迭代器
        vector         可变长数组      随机访问迭代器
        deque         双端队列          随机访问迭代器
        list               双向链表          双向迭代器    
        set / multiset                      双向迭代器 
        map / multimap                  双向迭代器  
        stack                                   不支持迭代器
        queue                                 不支持迭代器 

        ....

#include <iostream>
#include <array>
#include <list>

using namespace std;

int main() {

    // array是一个固定大小的数组,不能动态改变大小
    // array<int, 10> a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    array<int, 10> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // auto it = a.begin();
    array<int, 10>::iterator it = a.begin();
    while (it != a.end()) {
        cout << *it << " ";
        *it = 2 * (*it);
        ++it; // 比it++ 效率更高
    }
    cout << endl;

    //只有实现了迭代器的容器,才可以使用新式for循环
    for (auto it : a) {
        cout << it << " ";
    }
    cout << endl;

    cout << "=============================" << endl;

    array<int, 10>::reverse_iterator it1 = a.rbegin();
    while (it1 != a.rend()) {
        cout << *it1 << " ";
        *it1 = 2* (*it1);
        ++it1;
    }
    cout << endl;

    for (auto it : a) {
        cout << it << " ";
    }
    cout << endl;

    array<int, 10>::iterator it2 = a.begin();
    it2 += 3;
    cout << *it2 << endl;

    list<int> l{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    // auto it3 = l.begin();
    list<int>::iterator it3 = l.begin();
    // it3 += 3; // list的迭代器不支持随机访问
    cout << *it3 << endl;

    return 0;
}

迭代器辅助函数(算法):迭代器库---->迭代器操作

        STL中有常用的三个操作迭代器的函数模板

        

        p和q都是迭代器,n是整数
           
advance(p, n)        使迭代器p向前或者向后移动n个位置,无返回值
                                迭代器必须有双向迭代器的功能

            distance(p, q)
                            计算两个迭代器之间的距离,即迭代器p经过多少次++后能够和q相等
                            如果调用前,p已经在q的后面,如果满足随机访问迭代器,则返回p-->q距离的负数,如果不满足随机访问迭代器,则返回q到p的距离


            next        令迭代器自增

            prev        令迭代器自减,必须满足双向迭代器的要求

            iter_swap(p, q)        交换两个迭代器p,q指向的位置


        使用上面的函数,需要加上头文件<algorithm>

迭代器失效(迭代器非法化):
        迭代器表示一个位置,但是我们对容器的某些操作,可能会影响迭代器表示的值
        操作:插入/删除/增加元素......
        Linux和Windows失效的方式不一样!!!


        迭代器失效是序列式容器的一大特点(因为内存是连续的)

                对于序列式容器(如:vector/deque)实现的方式大多是连续的内存空间,删除当前的iterator指向的元素
                可能会使得容器中后面的元素内容改变,造成表示当前元素后面的迭代器都失效

                这是因为vector/deque使用了连续分配的内存,删除一个元素会使得后面所有的元素都向前移动一个位置
                所以不能使用erase(it++)的方式,但是erase可以返回下一个有效的iterator

                在迭代器的后面插入一个元素,可能会造成迭代器失效,也可能不会(空间不够:扩容)

        
        数组型数据结构:元素是分配在连续的内存中,insert和earse等操作,都会使得删除点和插入点后面的元素
                变动位置,所以插入点和删除点后面的迭代器都有可能失效

        链式型数据结构:元素是分配在不连续的内存中,insert和earse等操作,只会让删除点和插入点当前的迭代器失效

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <array>

using namespace std;

int main() {

    list<int> l{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    list<int>::iterator p = l.begin(); // 1

    cout << *p << endl;
    advance(p, -3);
    cout << *p << endl; // 9

    list<int>::iterator q = l.end();
    
    cout << distance(p, q) <<endl;
    // q已经在后面了,如果满足随机访问迭代器,则返回p-->q距离的负数
    // 如果不满足随机访问迭代器,则返回q到p的距离
    // cout << distance(q, p) << endl; // 9 list不支持随机访问迭代器

    // array<int, 5> aa{1, 2, 3, 4, 5};
    // cout << distance(aa.end(), aa.begin()) << endl; // -5

    vector<int> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    vector<int>::iterator it = a.begin();
    it = it + 3; // 4
    cout << *it << endl;
    a.erase(a.begin()); // 会使得某些迭代器失效
    cout << *it << endl; // 5

    a.erase(it++);
    cout << *it << endl; // 7

    it = a.erase(it); // 返回删除的元素的下一个有效元素的迭代器
    cout << *it << endl; // 8

    return 0;
}

重点:迭代器和指针之间的区别!!!

02 容器

容器就是用来存储并且管理某一类对象的集合,是使用类模板实现的常用的数据结构
        常见的有静态数组、可变长数组、链表、栈、队列、二叉树.....等数据结构在STL中都实现为模板类

在使用容器类模板的时候,需要指明容器中存放的元素类型是什么(模板类实例化的时候需要类型参数)
        如:

                list<int> l{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

                array<int, 10> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

                ......

容器可以存放基本数据类型,也可以存放自定义的数据类型(自己实现的类类型),对象或者基类类型的变量加入容器的时候,实际上加入到容器的是对象/变量的副本(自定义的类型要能够整体的拷贝和赋值)


STL中许多的算法,如:排序、查找...算法,在执行的过程中会对容器中的元素进行比较(<、<=、==......),元素在比较的时候,通常会使用运算符(被放到容器中的类型最好要实现必须使用的运算符)

容器分为两大类:

        顺序容器:
                如:数组(array、vector、deque(双端数组/双端队列))、链表(list)、栈(stack)、队列(queue)
                之所以被叫做顺序容器是因为
元素在容器中的位置与元素的值无关(和插入到容器的顺序有关),即容器本身不是排序的,将元素加入到容器的时候,指定在什么位置,元素就位于什么位置

        关联容器(底层使用排序二叉树实现的,关联容器中的元素是自动排序的):
                集合(set)以及映射(map)
                set、multiset、map、multimap
               
关联容器中的元素是自动排序的,插入元素的时候,容器会自动按照一定的顺序将元素放到合适的位置
               因为插入元素时,不能指定位置(所以放到关联容器内的数据类型要能够使用<、<=比较)

               默认情况下,关联容器中的元素是从小到大排序的,是使用"<"进行比较的(如果是自己实现的类型就需要提供重载的运算符函数)
              因为关联容器是排好序的,所以关联容器的查找性能非常好

03 基本的顺序容器的用法介绍

array

        静态数组类型

        需要包含头文件

                #include <ayyay>
        
        实例化方法(构造函数):

                array<int, 5> arr1;

                array<int, 10> arr2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

                array<int, 10> arr3{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

                array<string, 2> arr4 = {"hello", "world"};


        元素访问:

                at        访问指定的元素,同时进行越界检查

                        cout << arr2.at(0) << endl;

                        arr2.at(0) = 10;

                

                operator[]        通过下标的方式访问指定的元素

                        cout << arr2[1];

                        arr[2] = 1024;

                

                front        访问第一个元素

                        arr2.front();

                

                back        访问最后一个元素

                        arr2.back();

                

                data        直接返回底层存储元素的空间的首地址

                        int *pd = arr2.data();

                

        迭代器:是一种思想,是为了方便让用户简单的遍历容器中的元素

                以下是array给用户提供的获取迭代器的方法

                        begin();

                        end();

                        

                        cbegin();

                        cend();

                        

                        rbegin();

                        rend();

                        

                        crbegin();

                        crend();

                        

        容量:

                empty()        检查容器是否为空

                size()        返回容纳的元素数量

                max_size()        返回最大的容量

                

        操作:

                fill        以指定的值填充数组

                swap        交换两个数组的所有内容

        

        非成员函数:

                operator==        比较,会按照数组每一个元素进行比较

        

vector        可变长数组

        添加头文件:

                #include <vector>

        构造函数:

                vector<int> a1;

                vector<string> a2{10, "hello"};

                vector<int> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        

        元素访问:

                at        访问指定的元素,同时进行越界检查

                        a.at(0); ===> 1

                        a.at(1) = 100;

                

                operator[]        访问指定的元素

                        a[0]

                        a[1] = 100;

                

                front        访问第一个元素

                        a.front()

                        ===>

                        a[0] / a.at(0);

                

                back        访问最后一个元素

                        a.back() = 1024;

                        cout << a.back() << endl;

        

                data        直接访问底层数组

                        int *pb = a.data();

        

        迭代器:是一种思想,是为了方便的让用户可以简单的遍历容器中的元素

                以下是vector给用户提供的获取迭代器的方法

                        begin();

                        end();

                

                        cbegin();

                        cend();

                        

                        rbegin();

                        rend();

                

                        crbegin();

                        crend();

                        

        容量:

                empty()        检查容器是否为空

                        if (a.empty()) {

                                cout << "容器为空" << endl;

                        }

                        

                size        返回容纳的元素数

                        a.size();

                

                max_size        返回可容纳的最大元素数

                        a.max_size();

                

                reserve        预留存储空间

                        a.reserve(100);

                

                capacity        返回当前存储空间能够容纳的元素数

                

                shrink_to_fit        通过释放未使用的内存减少内存的使用        

                

        修改器:

                clear        清除内容

                        a.clear();

                

                insert        插入元素 <------

                

                emplace        原位构造元素

                

                erase        擦除元素

                

                push_back        将元素添加到容器末尾

                        

                emplace_back        在容器末尾就地构造元素

                

                pop_back        移除未元素

                

                resize        改变容器中可存储元素的个数

                

                swap        交换内容

                

        非成员函数(一个元素一个元素的比较)

                operator==

                operator!=

                operator<

                operator<=

                operator>

                operator>=

                operator<=>

#include <iostream>
#include <vector>
#include <list>
#include <iterator>

using namespace std;

int main() {

    vector<int> a1;
    for (auto it : a1) {
        cout << it << " ";
    }
    cout << endl;
    
    vector<string> a2{10, "hello"};
    for (auto it : a2) {
        cout << it << " ";
    }
    cout << endl;

    int x = 10;
    vector<int> a3{5, x};
    for (auto it : a3) {
        cout << it << " ";
    }
    cout << endl;

    vector<int> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    a.reserve(100);

    cout << "size:" << a.size() << endl;
    cout << "capacity:" << a.capacity() << endl;

    a.back() = 1024;
    cout << a.back() << endl;

    for (auto it : a) {
        cout << it << " ";
    }
    cout << endl;

    cout << a.max_size() << endl;

    return 0;
}

deque        可变长数组

list        双向链表

forward_list        单向链表

stack        栈

queue        队列

priority_queue        带优先级的队列(不是先进先出)

1. 重点了解,各种顺序容器之间的差别

        如:

                vector 和 array

                vector 和 deque

                deque 和 list

                queue 和 priority_queue

                ......


2. 容器的选择

        (1) 强调快速随机访问,则 vector 优于 list

        (2) 以知要存储的元素个数,则 vector 比 list 好

        (3) 强调增和删且不要在两端插入和修改,则 list 比 vector 更好

        (4) 需要在首部插入和删除元素,则 deque 比 vector 好               

04 关联容器(set / map内部是排好序的,和加入元素的顺序无关)

集合(set)是一种包含已经排序好的对象的关联容器

set / multiset会根据待定的排序规则(默认是从小到大),自动的将元素排序,两者不同在于前者不允许元素重复,而后者允许元素重复
        set<int> s;

        set<string> s1; // 逐个字母比较

        ......

        set<Test> s2; // Test 里面实现了比较规则,能够调用> / <


注意:

        1. 不能直接修改元素值,因为会打乱原本正确的顺序,要改变原始数据的值必须先删除旧元素,再插入新元素

        2. 不提供直接操作元素的任何函数,直接通过迭代器进行间接存取,而且从迭代器的角度看,元素值是常数(不可以直接修改)

                set 是使用红黑树实现的(平衡二叉树),内部的查找性能非常好

        

主要用来存储一些没有顺序的用户数据

用户能够自由的插入和查找

#include <iostream>
#include <set>

using namespace std;

int main() {

    // 实例化set对象,里面的元素类型是string
    set<string> a;
    a.insert("cat");
    a.insert("dog");
    a.insert("horse");
    a.insert("haha");
    a.insert("aaaaa");
    // set<string>::iterator it = a.begin();
    // while (it != a.end()) {
    //     cout << *it << ' ';
    //     ++it;
    // }
    for (auto it: a) {
        cout << it << ' ';
    }
    cout << endl;

    for (set<string>::iterator it = a.begin(); it != a.end(); ++it) {
        cout << *it << ' ';
        // *it = "bbb"; // set不允许修改元素
    }
    cout << endl;

    cout << "empty:" << a.empty() << endl;
    cout << "size:" << a.size() << endl;

    // struct pair {
    //     T1 first; // KEY
    //     T2 second; // VALUE
    // };
    std::pair<set<string>::iterator, bool> res = a.insert("zhangsan");
    cout << *(res.first) << endl; // 访问pair的第一个数据,指向插入后的数据
    cout << res.second << endl; // 是否插入成功

    res = a.insert("zhangsan");
    cout << *res.first << endl; // 访问pair的第一个数据,指向插入后的数据
    cout << res.second << endl; // 是否插入成功

    a.insert({"111", "222", "3333"});

    a.erase("111");

    for (auto it : a) {
        cout << it << ' ';
    }
    cout << endl;

    return 0;
}

aaaaa cat dog haha horse 
aaaaa cat dog haha horse 
empty:0
size:5
zhangsan
1
zhangsan
0
222 3333 aaaaa cat dog haha horse zhangsan

05 map(映射)

map是关联容器中的一种,map里面存储的元素一般是一个一个的键值对,在关联容器中,对象的位置取决于和它关联的键的值(以pair中的第一个数据排序)
键可以是基本类型,也可以是类类型,字符串经常被用来作为键


如:

        手机通讯录(名字和号码就是一个键值对)

                名字可能是一个字符串,对应着一个确定的电话号码,这种形式的数据叫做键值对

                称为pair类型的对象


        账号---密码也是键值对
 

pair 也是一个类模板,它包含两个成员变量,成员变量的类型可以任意指定
pair 的第一个成员叫做first,第二个成员叫做second
pair 的大致结构如下:

        template<typename T1, typename T2>

        class pair {

                T1 first; // KEY

                T2 second; // VALUE

        };

map最主要的作用也是查找,通过 pair 的一个元素(键)可以很快的找到与之关联的第二个元素(值),一般的我们把第一个元素叫做Key,第二个元素叫做value
        所以,一个pair结构就是一个键值对,map容器中存储的就是一个一个的键值对

map<K, T>类模板定义在头文件map中,它定义了一个保存T类型的map,每一个T类型的对象都有一个与之关联的K类型的键,容器中元素位置是根据键排序的,可以使用键在容器中查找键对应的值
        ====>
        Key是不能重复了,而Value是可以重复的

   

map和multimap唯一的区别在于,map的键值不可以重复,而multimap的键值可以重复

#include <iostream>
#include <map>
#include <iterator>

using namespace std;

// -std=c++14 
auto print = [](const auto & m) {
    for (auto it : m) {
        cout << it.first << "," << it.second << endl;
    }
};

int main() {

    // map<string, int> m;

    // m["zhangsan"] = 60; // 通过下标的形式往m中插入键值对  <"zhangsan",60>
    // m["lisi"] = 70;  // <"lisi",70>
    // m["kunkun"] = 80;

    // cout << m.size() << endl;
    // for (map<string, int>::iterator it = m.begin(); it!= m.end(); ++it) {
    //     cout << it->first << "," << it->second << endl;
    // }
    
    // cout << "====================" << endl;
    
    // m["kunkun"] = 98; // 如果下标不存在,则是插入数据,如果下标存在,则是修改“键”对应的值

    // for (auto it : m) {
    //     cout << it.first << "," << it.second << endl;
    // }

    // 创建一个容器对象
    map<string, string> ssmap;
    cout << "size:" << ssmap.size() << endl;

    // 插入键值对
    ssmap["you"] = "你"; // <"you","你"> []里面的一般不叫做下标,而是叫做key
    ssmap.insert({"hello", "你好"});
    ssmap.insert(make_pair("how", "如何"));
    ssmap.insert(pair<string, string>("world", "世界"));

    // 输出键值对
    for (auto it : ssmap) {
        cout << it.first << "," << it.second << endl;
    }
    cout << "size:" << ssmap.size() << endl;

    // 利用lamda表达式输出
    print(ssmap);

    // 修改
    ssmap["you"] = "你真棒";

    print(ssmap);

    // 可以使用at函数查找给定键对应的值,如果不存在,则抛出out_of_range异常
    try {
        // cout << ssmap.at("KKK") << endl;
        auto value = ssmap.at("you");
        cout << "value:" << value << endl;
    } catch (out_of_range & e) { // catch (exception & e) 也可以捕获
        cout << e.what() << endl;
    }

    // 使用find函数查找给定键对应的值,找到了则返回对应键值对的迭代器,否则返回end
    auto it = ssmap.find("you");
    if (it != ssmap.end()) {
        cout << "find:" << it->first << "," << it->second << endl;
    }

    return 0;
}

06 常用算法

指的是使用函数模板实现的常用的算法,如:排序,查找,交换,乱序...

算法一般是用来处理集合中的元素,可以出于不同的目的去查找、排序、和修改集合中的元素,所以容器的迭代器都提供一致的操作,通过迭代器的协助,算法程序可以作用于任何容器

STL提供了能够在各种容器中使用的算法,大约有70多种....

许多算法操作的是容器的一个区间(也可以是整个容器),因此算法可能需要两个/两个以上的参数,一个是区间的起点,一个是区间的终点,可能还需要一个"谓词对象(可调用对象)"来形容一个条件!!!


有的算法会返回一个迭代器

        如:find_if 算法在容器中查找一个符合条件的元素
        并且返回一个指向该元素的迭代器,如果没找到则返回区间末尾的迭代器,说明遍历完了还没有找到符合条件的元素

有的算法会修改容器指定数据:

        如:
                copy        将一个容器指定内容复制到另一个容器
                copy_if        将一个容器中符合条件的内容复制到另一个容器
                remove        在容器中删除一个元素
                random_shuffle        随机打乱容器中的元素
                ....


有的算法不会修改容器中的数据
        如:find 在容器中查找符合条件的元素
               count_if 统计容器中符合条件的元素(需要一个一元谓词)

 

注意掌握下面几个算法:
       
sort
            前面两个参数是排序的区间,第三个参数可以省略
            如果不省略,提供一个可调用对象(函数/函数指针/函数对象/lambda),说明排序规则(接受一个二元谓词)


        find
            查找指定的数据


        find_if 
            前面两个参数是查找的区间,第三个参数是你要查找的值,是一个可调用对象,描述了查找条件
            会找到第一个让可调用对象返回值的元素的迭代器

        count 
            统计指定的数据的个数

        count_if 
            统计容器中符合条件的元素数量,条件就是使用可调用对象描述!

 

不修改序列的操作

修改序列的操作

划分操作

排序操作

#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <algorithm>

using namespace std;

// 用自定义函数对象排序(函数对象,重载了())
struct {
    bool operator()(int a, int b) const {   
        return a < b;
    }
} customLess; // 利用类型定义了一个全局变量

int main() {

    // s是一个集合对象
    vector<int> s{67, 7, 4, 9, 15, 2, 6, 34, 100, 56};

    // 默认是升序排序
    std::sort(s.begin(), s.end());
    for (auto it : s) {
        std::cout << it << " ";
    }
    std::cout << '\n';

    // 用标准库比较函数对象排序
    // std::greater<int>()是一个函数对象 从大到小排序
    // std::less<int>()是一个函数对象 从小到大排序
    std::sort(s.begin(), s.end(), std::greater<int>());
    for (auto it : s) {
        std::cout << it << " ";
    }   
    std::cout << '\n';

    std::sort(s.begin(), s.end(), customLess);
    for (auto it : s) {
        std::cout << it << " ";
    }   
    std::cout << '\n';

    // 用 lambda 表达式排序
    std::sort(s.begin(), s.end(), [](int a, int b) {
        return a > b; });
    for (auto it : s) {
        std::cout << it << " ";
    } 
    std::cout << '\n';

    return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

bool fun(int x) {
    cout << "hahahaha" << endl;
    return x < 5;
}

int main() {

    std::vector<int> v{1, 2, 3, 4, 4, 3, 7, 8, 9, 10};
 
    // 确定 std::vector 中有多少个整数匹配目标值
    int num1 = std::count(v.begin(), v.end(), 3);
    int num2 = std::count(v.begin(), v.end(), 5);
    std::cout << "number: " << 3 << " count: " << num1 << '\n';
    std::cout << "number: " << 5 << " count: " << num2 << '\n';
 

    // 用 lambda 表达式计量能被 3 整除的元素数
    int num3 = std::count_if(v.begin(), v.end(), [](int i) { return i % 3 == 0; });
    std::cout << "number divisible by three: " << num3 << '\n';

    // 查找大于3的元素数量
    int res = std::count_if(v.begin(), v.end(), [](int x) { return x > 3; });
    std::cout << res  << '\n';

    // 查找小于5的元素数量
    res = std::count_if(v.begin(), v.end(), fun);
    std::cout << res  << '\n';

    return 0;
}

number: 3 count: 2
number: 5 count: 0
number divisible by three: 3
6
hahahaha
hahahaha
hahahaha
hahahaha
hahahaha
hahahaha
hahahaha
hahahaha
hahahaha
hahahaha
6
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值