C++ STL标准模板库学习笔记

64 阅读7分钟

C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数, 这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。

STL

  • 容器(Containers):vector、list、deque、queue、stack、set、map等。
  • 迭代器(Iterators):用于遍历容器中的元素。
  • 算法(Algorithms):包括排序、查找、统计、变换等一系列常用算法。
  • 函数对象(Function Objects):可用于算法中的比较、操作等。
  • 适配器(Adapters):包括容器适配器、迭代器适配器、函数适配器等,用于将现有的组件转化为其他需要的形式。
  • 其他组件:包括数值计算、随机数、日期和时间、文件操作等

容器

  • 序列式容器(Sequential Containers):按照线性方式组织元素,包括vector、list、deque等。
  • 关联式容器(Associative Containers):按照键值方式组织元素,包括set、multiset、map、multimap等。
  • 容器适配器(Container Adapters):用于将现有的容器转换为其他形式,包括stack、queue、priority_queue等。

vector

向量容器(一个 C++ 标准的模板),vector是一个动态数组,可以在运行时动态增加或减少其大小

#include <iostream>
#include <vector>

using namespace std;

// g++ -std=c++11 stl_vector_test.cpp
int main()
{
    vector<int> v;             // 创建一个空的int类型vector
    vector<string> v2(10);     // 创建一个包含10个空字符串的vector
    vector<double> v3(5, 2.5); // 创建一个包含5个值为2.5的double类型vector

    v.push_back(10);        // 向vector尾部添加一个元素
    v.insert(v.begin(), 5); // 在vector头部插入一个元素

    int x = v[0];    // 获取vector中第一个元素的值
    int y = v.at(1); // 获取vector中第二个元素的值

    v[0] = 20;    // 修改vector中第一个元素的值为20
    v.at(1) = 30; // 修改vector中第二个元素的值为30

    v.pop_back();       // 删除vector尾部的元素
    v.erase(v.begin()); // 删除vector头部的元素

    for (int i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }

    vector<int>::iterator iter = v.begin();
    while (iter != v.end())
    {
        cout << *iter << endl;
        iter++;
    }

    v.clear(); // 删除vector中所有的元素
    return 0;
}

list

list是一个双向链表,可以在运行时动态增加或删除节点

#include <iostream>
#include <list>

using namespace std;

// g++ -std=c++11 stl_list_test.cpp
int main()
{
    list<int> lst;             // 创建一个空的int类型list
    list<string> lst2(10);     // 创建一个包含10个空字符串的list
    list<double> lst3(5, 2.5); // 创建一个包含5个值为2.5的double类型list

    lst.push_back(10);          // 向list尾部添加一个元素
    lst.push_front(5);          // 向list头部添加一个元素
    lst.insert(lst.begin(), 3); // 在list头部插入一个元素

    int x = lst.front(); // 获取list中第一个元素的值
    int y = lst.back();  // 获取list中最后一个元素的值

    lst.front() = 20; // 修改list中第一个元素的值为20
    lst.back() = 30;  // 修改list中最后一个元素的值为30

    int size = lst.size(); // 获取list中元素的个数

    for (list<int>::iterator it = lst.begin(); it != lst.end(); it++)
    {
        cout << *it << " ";
    }

    lst.pop_front();        // 删除list头部的元素
    lst.pop_back();         // 删除list尾部的元素
    lst.erase(lst.begin()); // 删除list头部的元素

    lst.clear(); // 删除list中所有的元素
    return 0;
}

deque

deque是一个双端队列,可以在运行时动态增加或删除队首和队尾的元素;

#include <iostream>
#include <deque>

using namespace std;

// g++ -std=c++11 stl_deque_test.cpp
int main()
{
    deque<int> dq;             // 创建一个空的int类型deque
    deque<string> dq2(10);     // 创建一个包含10个空字符串的deque
    deque<double> dq3(5, 2.5); // 创建一个包含5个值为2.5的double类型deque

    dq.push_back(10); // 向deque尾部添加一个元素
    dq.push_front(5); // 向deque头部添加一个元素

    int x = dq.front(); // 获取deque中第一个元素的值
    int y = dq.back();  // 获取deque中最后一个元素的值

    dq.front() = 20; // 修改deque中第一个元素的值为20
    dq.back() = 30;  // 修改deque中最后一个元素的值为30

    dq.pop_front(); // 删除deque头部的元素
    dq.pop_back();  // 删除deque尾部的元素

    int size = dq.size(); // 获取deque中元素的个数

    for (deque<int>::iterator it = dq.begin(); it != dq.end(); it++)
    {
        cout << *it << " ";
    }

    dq.clear(); // 删除deque中所有的元素
}

list 和 deque 的区别

  • list是一个双向链表,每个元素都有前驱和后继指针,因此它的元素在内存中不是连续存储的。
  • deque是一个双端队列,内部实现是一个环形的缓冲区,因此它的元素在内存中是连续存储的。
  • 由于deque的元素在内存中是连续存储的,因此它支持随机访问,即可以像数组一样通过下标直接访问元素。而list不支持随机访问,必须通过迭代器遍历才能访问元素
  • 由于list的元素在内存中不是连续存储的,因此在插入和删除元素时,只需要修改前后节点的指针即可,不需要移动其他元素,因此list的插入和删除性能比deque更高
  • 而deque在插入和删除元素时,由于需要维护缓冲区的连续性,因此可能需要移动其他元素,因此其插入和删除性能相对较低
  • 由于list支持高效的插入和删除操作,因此适合于元素数量变化频繁的场景; 而deque支持随机访问,适合于需要频繁访问元素的场景。

map

map是一种关联容器,它提供了一种将键映射到值的方式

#include <iostream>
#include <map>

using namespace std;

// g++ -std=c++11 stl_map_test.cpp
int main()
{
    map<string, int> m; // 创建一个空的string到int的map

    m["apple"] = 10;                   // 将键"apple"映射到值10
    m.insert(make_pair("banana", 20)); // 将键"banana"映射到值20

    int x = m["apple"];     // 获取键"apple"对应的值
    int y = m.at("banana"); // 获取键"banana"对应的值

    m["apple"] = 20;     // 将键"apple"对应的值修改为20
    m.at("banana") = 30; // 将键"banana"对应的值修改为30

    m.erase("apple"); // 删除键为"apple"的元素

    int size = m.size(); // 获取map中元素的个数

    for (map<string, int>::iterator it = m.begin(); it != m.end(); it++)
    {
        cout << it->first << ": " << it->second << endl;
    }

    if (m.count("apple"))
    {
        cout << "apple exists" << endl;
    }
    return 0;
}

queue

queue是一种先进先出的容器,可以在队尾添加元素,在队头删除元素

#include <queue>
#include <iostream>

using namespace std;

// g++ -std=c++11 stl_queue_test.cpp
int main()
{
    queue<int> q;      // 创建一个空的int类型queue
    q.push(10);        // 向queue尾部添加一个元素
    int x = q.front(); // 获取queue头部的元素
    int y = q.back();  // 获取queue尾部的元素
    // q.pop();             // 删除queue头部的元素
    int size = q.size(); // 获取queue中元素的个数
    if (q.empty())
    {
        cout << "queue is empty" << endl;
    }
    while (!q.empty())
    {
        int x = q.front();
        cout << x << " ";
        q.pop();
    }
}

stack

stack 是一种后进先出的容器,可以在栈顶添加元素,在栈顶删除元素

#include <iostream>
#include <stack>

using namespace std;

// g++ -std=c++11 stl_stack_test.cpp
int main()
{
    stack<int> s;        // 创建一个空的int类型stack
    s.push(10);          // 向stack顶部添加一个元素
    int x = s.top();     // 获取stack顶部的元素
    s.pop();             // 删除stack顶部的元素
    int size = s.size(); // 获取stack中元素的个数
    if (s.empty())
    {
        cout << "stack is empty" << endl;
    }
    
    while (!s.empty())
    {
        int x = s.top();
        cout << x << " ";
        s.pop();
    }
    return 0;
}

set

set是一种关联容器,用于存储不重复的元素,且元素自动按照一定的规则排序

#include <iostream>
#include <set>

using namespace std;

// g++ -std=c++11 stl_set_test.cpp
int main()
{
    set<int> s;   // 创建一个空的int类型set
    s.insert(10); // 向set中插入一个元素

    set<int>::iterator it = s.find(10); // 查找set中是否存在值为10的元素
    if (it != s.end())
    {
        cout << "item exists" << endl;
    }
    s.erase(10);         // 删除set中值为10的元素
    int size = s.size(); // 获取set中元素的个数
    if (s.empty())
    {
        cout << "set is empty" << endl;
    }
    for (set<int>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << *it << " ";
    }
    return 0;
}

迭代器

迭代器(Iterator)用于遍历容器中的元素。迭代器是一种类似于指针的对象,可以指向容器中的某个元素,并支持类似于指针的操作,如解引用、自增等

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> v = {1, 2, 3, 4, 5}; // C++11特性, 编译时: g++ -std=c++11 source.cpp
    auto it = v.begin(); // 获取vector的起始迭代器

    // 迭代器的比较:
    auto end = v.end();
    while (it != end)
    {
        int x = *it; // 获取迭代器指向的元素的值
        cout << x << " ";
        it++; // 将迭代器指向下一个元素
    }

    // 迭代器的逆向遍历:
    auto it2 = v.rbegin(); // 获取vector的逆向迭代器
    auto end2 = v.rend();
    while (it2 != end2)
    {
        cout << *it2 << " ";
        it2++;
    }

    return 0;
}

g++ -std=c++11 source.cpp && ./a.out

算法

C++ STL(Standard Template Library)提供了多种算法(Algorithms),包括以下几类:

  • 非修改性序列算法(Non-modifying Sequence Algorithms):不会修改序列中的元素,包括查找、计数、比较等。
  • 修改性序列算法(Modifying Sequence Algorithms):可以修改序列中的元素,包括排序、拷贝、替换等。
  • 排列组合算法(Permutation Algorithms):用于生成序列的所有可能排列或组合。
  • 数值算法(Numeric Algorithms):用于对序列中的数值进行计算和操作,包括求和、平均数、内积等。
  • 堆算法(Heap Algorithms):用于操作堆数据结构,包括建堆、堆排序等。
  • 其他算法:包括特定于区间的算法、特定于集合的算法等。
#include <iostream>
#include <vector>

using namespace std;

// g++ -std=c++11 stl_algorithm_test.cpp && ./a.out
int main()
{
    vector<int> v = {1, 2, 3, 4, 5};
    auto it = find(v.begin(), v.end(), 3); // 查找值为3的元素
    if (it != v.end())
    {
        cout << "3 exists" << endl;
    }

    vector<int> v2 = {3, 1, 4, 1, 5, 9, 2, 6};
    sort(v2.begin(), v2.end()); // 对序列进行排序
    for (auto x : v2)
    {
        cout << x << " ";
    }

    return 0;
}