• C++ STL库的介绍和使用


    C++ STL库的介绍和使用

    STL(标准模板库),是惠普实验室开发的一系列软件的统称。现在主要出现在C++中,但是在引入C++之前该技术已经存在了很长时间了。STL从广义上分为:容器(container) 算法(algorithm) 迭代器(iterator),容器和算法之间通过迭代器进行无缝衔接。STL几乎所有的代码都采用了模板类或者是模板函数,这相比于创痛的由函数和类组成的库来说提供了更好的代码重用机会。STL标准模板库,在我们C++标准程序库中隶属于STL的占到了80%以上。

    STL六大组件

    • 容器:各种数据结构 vector list deque set map等,存放数据

    • 算法:如sort find copy for_each等,操作数据

    • 迭代器:容器和算法的桥梁

    • 仿函数:为算法提供更多的策略

    • 适配器:为算法提供更多的参数接口

    • 空间配置器:管容器和算法的空间

    STL六大组件的交互关系:容器通过空间配置器获取数据存储空间,算法通过迭代器获取存储器的内容,仿函数可以协助算法完成不同的策略变化,适配器可以修饰仿函数。

    算法的分类

    质变算法:运算过程中会更好区间内的数据,如拷贝、替换、删除等

    非质变算法:运算过程中不会更改区间内容,如查找、计数、遍历等

    迭代器

    迭代器是一种抽象的概念,提供一种方法,使之能够依顺序访问某个容器所含的各个元素,而无需暴露该容器的内部表示方式。

    迭代器的设计思维是STL的关键所在,STL中心思想是把容器和算法分开,彼此设计独立。

    一个简单的例子

    #include 
    #include 
    #include 
    using namespace std;
    void print(int it)
    {
        cout << it << " ";
    }
    void test()
    {
        vector<int> vv;
        vv.push_back(1);
        vv.push_back(2);
        vv.push_back(3);
    
        // 开始迭代器,指向开始位置
        vector<int>::iterator itBegin = vv.begin();
        // 结束迭代器,指向尾元素的下一个位置
        vector<int>::iterator itEnd = vv.end();
        for(auto it = itBegin; it!= itEnd; it++)
        {
            cout << *it << " ";
        }
        cout << endl;
        // 需要了解,可用vs查看对应的实现源码,可以看到整体实现思路是上面的for
        for_each(itBegin, itEnd, print);
        cout << endl;
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    容器和自定义类型

    #include 
    #include 
    #include 
    using namespace std;
    class Person
    {
    public:
        Person(int a, int n): age(a), num(n) {}
        int age;
        int num;
    };
    void print(const Person &it)
    {
        cout << it.age << " " << it.num << endl;
    }
    
    void test()
    {
        vector<Person> vv;
        vv.push_back(Person(1,1));
        vv.push_back(Person(2,2));
        vv.push_back(Person(3,3));
        for_each(vv.begin(), vv.end(), print);
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    容器嵌套容器

    #include 
    #include 
    #include 
    using namespace std;
    void print2(int a)
    {
        cout << a << " " ;
    }
    void print(const vector<int> &a)
    {
        for_each(a.begin(), a.end(), print2);
    }
    void test()
    {
        vector<int> a;
        a.push_back(1);
        a.push_back(2);
        a.push_back(3);
        vector<int> b;
        b.push_back(4);
        b.push_back(5);
        b.push_back(6);
        vector<int> c;
        c.push_back(7);
        c.push_back(8);
        c.push_back(9);
        vector<vector<int>> aa;
        aa.push_back(a);
        aa.push_back(b);
        aa.push_back(c);
        for_each(aa.begin(), aa.end(), print);
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    常用容器

    string

    C风格的字符串太过复杂,难以掌握,不太适合大程序的开发,所以C++标准库定义了一种string类,定义在头文件中。

    string和C风格字符串对比:

    • char是一个指针,string是一个类,string封装了char,管理了这个字符串,是一个char类型的容器。

    • string封装了很多实用的成员方法,查找find、拷贝copy、删除delete、替换replace、插入insert

    • 不用考虑内存的释放和越界,string管理了char*所分配的内存,每一次string的赋值,取值都由string类负责维护,不用担心复制越界和取值越界等算法

    下面的例子列举了一些简单使用:

    #include 
    #include 
    #include 
    using namespace std;
    // 字符串赋值
    void test()
    {
        string str("5152");
        string str3;
        str3 = str;
        cout << str3 << endl;
        string str2;
        str2 = "hello world";
        cout << str2 << endl;
        string str1;
        str1 = 'H';
        cout << str1 << endl;
        string str4;
        str4.assign(str);
        cout << str4 << endl;
    }
    // 字符串存取
    void test02()
    {
        string str = "hello world";
        cout << str[1] << endl;
        cout << str.at(1) << endl;
        str[1] = 'E';
        str[7] = 'O';
        cout << str << endl;
        try{
    //        str[100] = 'H';
            str.at(100) = 'H';
        }
        catch (exception &e)
        {
            cout << e.what() << endl;
        }
    }
    // 字符串拼接
    void test03()
    {
        string str1 = "hello ";
        string str2 = "world";
        str1+=str2;
        cout << str1 << endl;
        string str3 = "hello ";
        str3+="world";
        cout << str3 << endl;
        str3.append(" mmmm");
        cout << str3 << endl;
        str3.append(" mmmm", 3);
        cout << str3 << endl;
        str3.append(str2, 2, 3);
        cout << str3 << endl;
    }
    // 字符串查找和替换
    void test04()
    {
        string str1 = "hello world";
        int a = str1.find('e');
        cout << a << endl;
        a = str1.find('e', 3);
        cout << a << endl;
        a = str1.find("worl");
        cout << a << endl;
        // 表示从0开始替换5个字符,替换成后面的
        str1.replace(0, 5, "mmmmm");
        cout << str1 << endl;
    }
    // 字符串比较和子串的提取
    void test05()
    {
        string str1 = "hello world";
        string str2 = "hello";
        int a = str1.compare(str2);
        cout << a << endl;
        a = str1.compare("oooo");
        cout << a << endl;
        a = str1.compare("hello world");
        cout << a << endl;
    
        string str = str1.substr(0, 8);
        cout << str << endl;
        string str11 = "asd:asdas:asdasdasd:dadssad:dasdsa";
        int pos = 0;
        while(1)
        {
            int ret = str11.find(":", pos);
            if(ret < 0)
            {
                string tmp = str11.substr(pos, str11.size() - pos);
                cout << tmp << endl;
                break;
            }
            string tmp = str11.substr(pos, ret - pos);
            cout << tmp << endl;
            pos = ret + 1;
        }
    }
    // 字符串的插入和删除
    void test06()
    {
        string str = "hello world";
        str.insert(5, " hehe");
        cout << str << endl;
        str.erase(5, 5);
        cout << str << endl;
        str.clear();
        cout << str << endl;
    }
    // 字符串和C风格字符串转换
    void test07()
    {
        char *aa = "hello world";
        string str = aa;
        const char *cc = str.c_str();
        cout << str << endl;
        cout << cc << endl;
    }
    int main(int argc, char* argv[])
    {
        test07();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125

    vector

    vector的数据安排以及操作方式与数组相似,两者的唯一差别在于运用的灵活性。数组是静态空间,一旦配置了就不能改变,要更换大一点或者小一点的空间,可以,但是需要自己去做空间分配和数据拷贝,然后释放原先的空间。vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此vector的运用对于内存的合理利用与利用的灵活性有很大的帮助,我们不必担心空间不足而开辟一块很大的array。

    vector的迭代器:随机访问迭代器

    随机访问迭代器:迭代器+n可以通过编译器编译,就是随机访问迭代器。

    vector的容量(capacity)和大小(size)是有区别的。

    • capacity:是空间能容纳元素的最大个数

    • size:是空间中实际存放的个数

    #include 
    #include 
    #include 
    using namespace std;
    void print(const vector<int> &aa)
    {
        for(auto it = aa.begin(); it != aa.end(); it++)
        {
            cout << *it << " ";
        }
        cout << endl;
        fflush(stdout);
    }
    void test()
    {
        vector<int> v;
        for(int i = 0; i < 100; ++i)
        {
            v.push_back(i);
        }
        cout << v.size() << endl;
        cout << v.capacity() << endl;
    }
    // 另寻空间的次数
    void test02()
    {
        int count = 0;
        vector<int> v;
        int *p = nullptr;
        for(int i = 0; i < 1000; ++i)
        {
            v.push_back(i);
            if(p!= &v[0])
            {
                count++;
                p = &v[0];
            }
        }
        cout << count << endl;
    }
    // vector的构造、赋值和交换
    void test03()
    {
        vector<int> v(10, 5);
        print(v);
        vector<int> v1(v.begin() + 2, v.end() -2);
        print(v1);
        vector<int> v2(v);
        print(v2);
        vector<int> v3;
        v3.assign(v.begin() + 1, v.end() - 1);
        print(v3);
        vector<int> v4;
        v4 = v3;
        print(v4);
        vector<int> v5;
        v5.assign(5, 10);
        print(v5);
        vector<int> v6(6, 20);
        print(v6);
        v5.swap(v6);
        print(v5);
        print(v6);
    }
    // vector大小操作
    void test04()
    {
        vector<int> v(10, 5);
        if(v.empty())
        {
            cout << "v is empty" << endl;
        }
        else
        {
            cout << v.capacity() << endl;
            cout << v.size() << endl;
        }
        print(v);
        v.resize(16);
        print(v);
        v.resize(1024, 5);
        print(v);
        v.resize(2);
        print(v);
        cout << v.capacity() << endl;
        cout << v.size() << endl;
        // 使用swap收缩容量空间,通过交换函数重新申请大小
        vector<int>(v).swap(v);
        cout << v.capacity() << endl;
        cout << v.size() << endl;
        vector<int> vv(10, 5);
        cout << "vv:" << vv.capacity() << endl;
        cout << "vv:" << vv.size() << endl;
        // 空间预留
        vv.reserve(50);
        cout << "vv:" << vv.capacity() << endl;
        cout << "vv:" << vv.size() << endl;
    }
    // vector的数据操作
    void test05()
    {
        vector<int> v;
        for(int i = 0; i < 6; i++)
        {
            v.push_back(i);
        }
        cout << v[5] << endl;
        cout << v.at(5) << endl;
        cout << v.front() << endl;
        cout << v.back() << endl;
        v.insert(v.begin() + 3, 2, 100);
        print(v);
        v.pop_back();
        print(v);
        v.erase(v.begin()+1,v.begin()+3);
        print(v);
        v.erase(v.begin());
        print(v);
        v.clear();
        print(v);
    }
    int main(int argc, char* argv[])
    {
        test05();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126

    注意: resize只能修改size,不能修改容量。

    deque

    vector容器时一段连续的内存空间,dequeue则是一种双向开口的联系线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作。vector也可以在头尾进行插入操作 ,但是vector的头部插入操作效率奇差,不可以被接受。

    deque容器是由一段一段的定量连续空间构成的,一旦有必要在deque前端或者是尾端增加新的空间,便配置一段连续定量的空间,串接在deque的头端或者是尾端。deque最大的工作就是维护这些分段连续的空间的整体性的假象,并提供随机访问接口,避免了重新配置空间,复制,释放的轮回,代价就是复杂的迭代器架构。

    #include 
    #include 
    #include 
    using namespace std;
    void print(const deque<int> &aa)
    {
        for(auto it = aa.begin(); it != aa.end(); it++)
        {
            cout << *it << " ";
        }
        cout << endl;
        fflush(stdout);
    }
    // 初始化和赋值
    void test()
    {
        deque<int> v(5, 10);
        print(v);
        deque<int> v1;
        v1.assign(v.begin()+1, v.end()-1);
        print(v1);
        deque<int> v2 = v;
        print(v2);
        v2.swap(v1);
        print(v1);
        print(v2);
    }
    // 插入和存取
    void test02()
    {
        deque<int> v(5, 10);
        v.resize(10,3);
        print(v);
        v.resize(3);
        print(v);
        v.push_front(6);
        print(v);
        v.push_back(6);
        print(v);
        v.pop_front();
        print(v);
        v.pop_back();
        print(v);
        cout << v[1] << endl;
        cout << v.at(1) << endl;
        cout << v.size() << endl;
        cout << v.empty() << endl;
        v.clear();
        cout << v.empty() << endl;
    }
    int main(int argc, char* argv[])
    {
        test02();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    stack

    stack是一种先进后出的数据结构,它只有一个出口。stack允许新增元素,移除元素,取得栈顶元素,但是除了顶端外,没有任何办法 存取stack元素。换言之,stack不允许有遍历行为。有元素推入栈的操作称为push, 将元素推出栈的操作称为pop。

    stack没有迭代器。

    #include 
    #include 
    #include 
    using namespace std;
    void test()
    {
        stack<int> ss;
        ss.push(1);
        ss.push(2);
        ss.push(3);
        ss.push(4);
        ss.push(5);
        cout << ss.top() << endl;
        ss.pop();
        cout << ss.top() << endl;
        ss.pop();
        cout << ss.top() << endl;
        ss.pop();
        cout << ss.top() << endl;
        ss.pop();
        cout << ss.top() << endl;
        ss.pop();
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    queue

    队列是一种先进先出的数据结构,他有一个出口一个入口,queue容器允许从一端新增元素,从另外一端移除元素。

    队列没有迭代器。

    #include 
    #include 
    #include 
    using namespace std;
    void test()
    {
        queue<int> qq;
        qq.push(1);
        qq.push(2);
        qq.push(3);
        qq.push(4);
        qq.push(5);
        cout << qq.size() << endl;
        cout << qq.empty() << endl;
        cout << qq.front() << endl;
        qq.pop();
        cout << qq.front() << endl;
        qq.pop();
        cout << qq.front() << endl;
        qq.pop();
        cout << qq.front() << endl;
        qq.pop();
        cout << qq.front() << endl;
        qq.pop();
        cout << qq.empty() << endl;
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    list

    链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的。链表由一系列节点组成,节点在运行时动态生成。每个节点包括两个部分:一个是存储数据元素的数据域,一个是存储下一个节点地址的指针域。相较于vector的连续线性空间,list就显得负责许多,它的每次插入或者是删除一个元素,就是配置或者释放一个元素的空间。因此,list对于空间的运用具有绝对的精准,一点也不浪费,而且,对于任何位置的元素插入或者是删除,list永远是常数时间。list和vector是两个最长被使用的容器。list是一个双向链表。

    链表采用的是动态存储分配,不会造成内存浪费和溢出,链表执行插入和 删除操作十分方便,修改指针即可,不需要移动大量的元素。俩表灵活但是空间和时间额外耗费比较大。

    list的迭代器是 双向访问迭代器

    #include 
    #include 
    #include 
    #include 
    using namespace std;
    void print(const list<int> &ll)
    {
        for(auto & a : ll)
        {
            cout << a << " ";
        }
        cout << endl;
        fflush(stdout);
    }
    class Person
    {
    public:
        Person(int a) : age(a){}
        bool operator<(const Person &right)
        {
            return this->age < right.age;
        }
        int age;
    };
    class Compare
    {
    public:
        bool operator()(const Person &left, const Person &right)
        {
            return left.age < right.age;
        }
    };
    bool operator==(const Person &left, const Person &right)
    {
        return left.age == right.age;
    }
    bool compare(const Person &left, const Person &right)
    {
        return left.age > right.age;
    }
    void print(const list<Person> &ll)
    {
        for(auto & a : ll)
        {
            cout << a.age << " ";
        }
        cout << endl;
        fflush(stdout);
    }
    void print(const vector<Person> &ll)
    {
        for(auto & a : ll)
        {
            cout << a.age << " ";
        }
        cout << endl;
        fflush(stdout);
    }
    void test()
    {
        list<int> ll;
        ll.push_back(10);
        ll.push_back(20);
        ll.push_back(30);
        ll.push_back(40);
        print(ll);
        auto it = find(ll.begin(), ll.end(), 20);
        ll.insert(++it, 3 ,100);
        print(ll);
        ll.reverse();
        print(ll);
        ll.sort();
        print(ll);
    }
    void test01()
    {
        list<Person> ll;
        ll.push_back(10);
        ll.push_back(20);
        ll.push_back(30);
        ll.push_back(40);
        print(ll);
        ll.remove(Person(20));
        print(ll);
        ll.sort();
        print(ll);
        ll.sort(compare);
        print(ll);
    }
    void test02()
    {
        vector<Person> ll;
        ll.push_back(20);
        ll.push_back(10);
        ll.push_back(30);
        ll.push_back(40);
        sort(ll.begin(), ll.end(), compare);
        print(ll);
        sort(ll.begin(), ll.end(), Compare());
        print(ll);
        sort(ll.begin(), ll.end(), [&](const Person &left, const Person &right) -> bool {
            return left.age > right.age;
        });
        print(ll);
    }
    int main(int argc, char* argv[])
    {
        test02();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    注意: list删除自定义数据,必须重载==运算符,否则无法匹配是否相等。

    set/multiset

    set的特性是:所有元素都会根据元素的键值自动排序。set元素不像map那样可以同时拥有键和值,set元素既是键又是值。set不允许两个元素拥有同样的键值。

    set不可以通过迭代器修改set的值,set的值也是键,关系到set元素的排序规则。

    multiset的特性和用法和set完全相同,唯一不同的是multiset允许键值重复。set和multiset的底层实现是红黑树,红黑书是平衡二叉树的一种。

    #include 
    #include 
    #include 
    using namespace std;
    void print(const set<int> &ll)
    {
        for(auto & a : ll)
        {
            cout << a << " ";
        }
        cout << endl;
        fflush(stdout);
    }
    class Compare
    {
    public:
        bool operator()(const int& p1,const int& p2) const//一定要定义为常函数,且参数需要限定为const
        {
            return p1 > p2;
        }
    };
    void print(const set<int, Compare> &ll)
    {
        for(auto & a : ll)
        {
            cout << a << " ";
        }
        cout << endl;
        fflush(stdout);
    }
    void test()
    {
        set<int> ss;
        ss.insert(2);
        ss.insert(3);
        ss.insert(45);
        ss.insert(10);
        ss.insert(2);
        print(ss);
        cout << ss.size() << endl;
        ss.erase(3);
        print(ss);
        cout << ss.count(2) << endl;
        multiset<int> ss2;
        ss2.insert(2);
        ss2.insert(3);
        ss2.insert(45);
        ss2.insert(10);
        ss2.insert(2);
        ss2.erase(3);
        cout << ss2.count(2) << endl;
        auto it = ss.find(5);
        it != ss.end()? cout << "找到了" << endl : cout << "没有找到" << endl;
        auto it1 = lower_bound(ss.begin(), ss.end(), 10);
        auto it2 = upper_bound(ss.begin(), ss.end(), 10);
        auto mm = ss.equal_range(30);
    }
    void test01()
    {
        set<int, Compare> ss;
        ss.insert(2);
        ss.insert(6);
        ss.insert(8);
        print(ss);
    }
    int main(int argc, char* argv[])
    {
        test01();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    注意: set存放自定义数据类型的时候必须指定排序规则。

    pair

    对组是将一对值组合成一个值,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个公有属性first和second进行访问。

    #include 
    #include 
    using namespace std;
    void test()
    {
        pair<int, int> pp(1, 2);
        cout << pp.first << " " << pp.second << endl;
        pair<int, int> pp1 = make_pair(1, 2);
        cout << pp1.first << " " << pp1.second << endl;
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    map/multimap

    map的特性是所有元素会根据元素的键值自动排序。map所有的元素都是pair,同时拥有实值和键值,pair的第一个元素是键值,第二个元素被视为实值,map不允许两个元素有相同的键值。

    map的键值不可变,实值是可变的。

    multimap和map的操作类似,唯一的区别就是multimap键值可重复。map和multimap都是以红黑树为底层实现机制。

    #include 
    #include 
    #include 
    using namespace std;
    void print(const map<int, int> &ll)
    {
        for(auto & a : ll)
        {
            cout << a.first << " " << a.second << " " << endl;
        }
        fflush(stdout);
    }
    void test()
    {
        int a = 10;
        int &b = a;
        a = 20;
        cout << a << endl;
        cout << b << endl;
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    容器的使用时机

    典型的存储结构可随机存取
    vector单端数组
    deque双端数组
    list双向列表
    set/multiset红黑树
    map/multimap红黑树否(针对于ke)
    元素查询元素插入删除
    vector尾部
    deque两端
    list非常慢任何位置
    set/multiset
    map/multimap

    函数对象(仿函数)

    重载函数调用操作符的对象,其对象常称为函数对象,即他们是行为类似于函数的对象,也叫仿函数,其实就是重载了"()"操作符,使得对象可以像函数那样调用。

    注意:

    • 函数对象是一个类,不是一个函数。
    • 函数对象重载了"()"操作符,使他可以像函数一样调用。

    分类:

    • 如果一个函数重载了"()"且需要一个参数,则称为一元仿函数

    • 如果一个函数重载了"()"且需要两个参数,则称为二元仿函数

    总结:

    • 函数对象通常不定义构造函数和析构函数,所以在构造和析构的时候不会发生任何问题,避免了函数调用的运行时问题。
    • 函数对象超出了普通函数的概念,函数对象可以有自己的状态
    • 函数对象可以内联编译,性能好。用函数指针几乎不可能
    • 模板函数对象使得函数对象具有通用性,这就是他的优势之一

    谓词

    指的是普通函数或者是仿函数的返回值是bool类型的函数对象。

    如果operator接受一个参数,则叫一元谓词,如果接受2个参数则称为二元谓词。

    #include 
    #include 
    #include 
    using namespace std;
    bool findBigThan10(int a)
    {
        return a > 10;
    }
    class Find20
    {
    public:
        bool operator()(int v)
        {
            return v > 20;
        }
    };
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(40);
        vv.push_back(50);
        vv.push_back(60);
        auto ret = find_if(vv.begin(), vv.end(), findBigThan10);
        auto ret1 = find_if(vv.begin(), vv.end(), Find20());
        cout << *ret << endl;
        cout << *ret1 << endl;
    }
    bool compare(int left, int right)
    {
        return left > right;
    }
    class Compare
    {
    public:
        bool operator()(int left, int right)
        {
            return left < right;
        }
    };
    void test02()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(40);
        vv.push_back(50);
        vv.push_back(60);
        sort(vv.begin(), vv.end(), compare);
        for_each(vv.begin(), vv.end(), [](int val){
            cout << val << " ";
        });
        cout << endl;
        sort(vv.begin(), vv.end(), Compare());
        for_each(vv.begin(), vv.end(), [](int val){
            cout << val << " ";
        });
        cout << endl;
    }
    int main(int argc, char* argv[])
    {
        test02();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    内建函数对象

    STL内建了一些函数对象,分为:算数类函数对象,关系运算类函数对象,逻辑运算类仿函数。这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数功能。

    下面列出一些简单的例子。如果想要查看内建的函数,可以到对应的文件内查看对应的函数原型。

    #include 
    #include 
    #include 
    using namespace std;
    bool findBigThan10(int a)
    {
        return a > 10;
    }
    class Find20
    {
    public:
        bool operator()(int v)
        {
            return v > 20;
        }
    };
    void test()
    {
        plus<int> p;
        cout << p(10, 20) << endl;
        cout << plus<int>()(50, 20) << endl;
        minus<int> p1;
        cout << p1(10, 20) << endl;
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    适配器

    bind2nd和bind1st

    #define _HAS_AUTO_PTR_ETC 1
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    void print(int a, int b)
    {
        cout << a << " " << b << " " << endl;
    }
    class Print : public binary_function<int, int, void>
    {
    public:
        void operator()(int a, int b) const
        {
            cout << a << " " << b << " " << endl;
        }
    };
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(40);
        for_each(vv.begin(), vv.end(), bind1st(Print(), 5));
        for_each(vv.begin(), vv.end(), bind2nd(Print(), 5));
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    not1 和 not2 mem_fun_ref和ptr_fun

    这里列出标题,不做代码展示

    算法

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

    常用的遍历算法

    • for_each
    • transform
    #define _HAS_AUTO_PTR_ETC 1
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    int mvTransform(int a)
    {
        return a;
    }
    class Print : public binary_function<int, int, void>
    {
    public:
        void operator()(int a, int b) const
        {
            cout << a << " " << b << " " << endl;
        }
    };
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(40);
        vector<int> bb;
        bb.resize(vv.size());
        transform(vv.begin(), vv.end(), bb.begin(), mvTransform);
        for_each(bb.begin(), bb.end(), bind2nd(Print(), 5));
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    常用的查找算法

    • find
    • find_if
    • adjacent_find 查找相邻的重复元素
    • binary_find 二分查找,前提是容器必须有序
    • count 统计元素出现次数
    • count_if 按照条件统计次数
    #define _HAS_AUTO_PTR_ETC 1
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    class Person
    {
    public:
        Person(int a) : age(a) {}
        int age;
    };
    bool operator==(const Person &left, const Person &right)
    {
        return left.age == right.age;
    }
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(30);
        auto it = adjacent_find(vv.begin(), vv.end());
        if(it != vv.end())
        {
            cout << *it << endl;
        }
        vector<Person> vp;
        vp.push_back(50);
        vp.push_back(50);
        auto itP = adjacent_find(vp.begin(), vp.end());
        if(itP != vp.end())
        {
            cout << itP->age << endl;
        }
    
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    其他

    • merge
    • sort
    • random_shuffle 打乱
    • reverse 反转
    • copy
    • replace
    • replace_if
    • swap 交换函数
    #define _HAS_AUTO_PTR_ETC 1
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(30);
        vv.push_back(40);
        vv.push_back(40);
        vector<int> v2;
        v2.push_back(10);
        v2.push_back(30);
        vector<int> v3;
        v3.resize(vv.size() + v2.size());
        merge(vv.begin(), vv.end(), v2.begin(), v2.end(), v3.begin());
        for_each(v3.begin(), v3.end(), [](int a){
            cout << a << " ";
        });
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    算数生成算法

    • accumulate 累加求和
    • fill
    #define _HAS_AUTO_PTR_ETC 1
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(10);
        int sum = accumulate(vv.begin(), vv.end(), 0);
        cout << sum << endl;
        fill(vv.begin(), vv.end(), 20);
        for_each(vv.begin(), vv.end(), [](int a){ cout << a << endl; });
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    集合算法

    • set_intersection 求交集
    #define _HAS_AUTO_PTR_ETC 1
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(40);
        vector<int> v2;
        v2.push_back(30);
        v2.push_back(40);
        v2.push_back(50);
        v2.push_back(60);
        vector<int> v3;
        v3.resize(4);
        auto it = set_intersection(vv.begin(), vv.end(), v2.begin(), v2.end(), v3.begin());
        int size = 4 - (v3.end() - it);
        v3.resize(size);
        for_each(v3.begin(), v3.end(), [](int a){ cout << a << endl;});
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • set_union 求交集
    #define _HAS_AUTO_PTR_ETC 1
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(40);
        vector<int> v2;
        v2.push_back(30);
        v2.push_back(40);
        v2.push_back(50);
        v2.push_back(60);
        vector<int> v3;
        v3.resize(8);
        auto it = set_union(vv.begin(), vv.end(), v2.begin(), v2.end(), v3.begin());
        int size = 8 -(v3.end() - it);
        v3.resize(size);
        for_each(v3.begin(), v3.end(), [](int a){ cout << a << endl;});
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • set_difference 求差集
    #define _HAS_AUTO_PTR_ETC 1
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    void test()
    {
        vector<int> vv;
        vv.push_back(10);
        vv.push_back(20);
        vv.push_back(30);
        vv.push_back(40);
        vector<int> v2;
        v2.push_back(30);
        v2.push_back(40);
        v2.push_back(50);
        v2.push_back(60);
        vector<int> v3;
        v3.resize(4);
        auto it = set_difference(vv.begin(), vv.end(), v2.begin(), v2.end(), v3.begin());
        int size = 4 -(v3.end() - it);
        v3.resize(size);
        for_each(v3.begin(), v3.end(), [](int a){ cout << a << endl;});
    }
    int main(int argc, char* argv[])
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    Linux应用层开发(三)进程与线程简述
    递归查找树形状结(利用steam流的方式)leval值标明
    一体化伺服电机在三轴钻孔机中的应用
    蓝桥杯实战应用【算法代码篇】-次数差(附Java、Python和C++代码)
    抖音排名怎么做的?抖音视频排名规则是什么样的
    JJJ:添加开机自启动项
    MYSQL 基本操作 (2)
    ICPC 2022网络赛(1) - D Find the Number(暴搜预处理)
    进程管理(三)——进程间通信和多线程问题
    翻译软件-免费翻译软件-自动批量翻译软件
  • 原文地址:https://blog.csdn.net/turbolove/article/details/132713577