c++ STL

C++ 标准模板库(STL)是 C++ 的一个重要组成部分,它提供了许多通用数据结构和算法,用于简化和加速程序开发。STL 主要由容器、算法和迭代器组成,下面是对这些主要部分的详细介绍:

1. 容器(Containers)

容器是用来存储数据的数据结构,STL 提供了多种容器,每种容器都有其特定的用途和性能特点。

序列容器(Sequence Containers)

std::vector: 动态数组,支持快速随机访问和动态增删元素。

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    vec.push_back(6);
    for (int i : vec) {
        std::cout << i << " ";
    }
    return 0;
}

std::deque: 双端队列,支持在两端高效地插入和删除元素。

#include <deque>
#include <iostream>

int main() {
    std::deque<int> deq = {1, 2, 3, 4, 5};
    deq.push_front(0);
    deq.push_back(6);
    for (int i : deq) {
        std::cout << i << " ";
    }
    return 0;
}

std::list: 双向链表,支持在任意位置高效地插入和删除元素。

#include <list>
#include <iostream>

int main() {
    std::list<int> li = {1, 2, 3, 4, 5};
    li.push_front(0);
    li.push_back(6);
    for (int i : li) {
        std::cout << i << " ";
    }
    return 0;
}

std::forward_list: 单向链表,与 std::list 类似,但只能单向遍历。

#include <forward_list>
#include <iostream>

int main() {
    std::forward_list<int> flist = {1, 2, 3, 4, 5};
    flist.push_front(0);
    for (int i : flist) {
        std::cout << i << " ";
    }
    return 0;
}
  • 关联容器(Associative Containers)

std::set:有序集合,元素按照键值排序,不允许重复元素。

#include <set>
#include <iostream>

int main() {
    std::set<int> myset = {3, 1, 4, 1, 5, 9};
    for (int i : myset) {
        std::cout << i << " ";
    }
    return 0;
}

std::multiset:多重集容器,可以存储重复的元素,并按照升序进行排序

#include <set>
#include <iostream>

int main() {
    std::multiset<int> myset = {3, 1, 4, 1, 5, 9};
    for (int i : myset) {
        std::cout << i << " ";
    }
    return 0;
}

std::map: 有序映射,键值对按照键排序,不允许重复键。

#include <map>
#include <iostream>
#include <string>

int main() {
    std::map<int, std::string> mymap = {{1, "one"}, {2, "two"}, {3, "three"}};
    for (auto& pair : mymap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    return 0;
}

std::multimap:

#include <map>
#include <iostream>
#include <string>

int main() {
    std::multimap<int, std::string> mymap = {{1, "one"}, {2, "two"}, {1, "uno"}};
    for (auto& pair : mymap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    return 0;
}
  • 无序容器(Unordered Containers)

std::unordered_set, std::unordered_multiset: 无序集合,元素无序存储,不允许、允许重复元素。

#include <unordered_set>
#include <iostream>

int main() {
    std::unordered_set<int> myset = {3, 1, 4, 1, 5, 9};
    for (int i : myset) {
        std::cout << i << " ";
    }
    return 0;
}

std::unordered_map, std::unordered_multimap: 无序映射,键值对无序存储,不允许、允许重复键。

#include <unordered_map>
#include <iostream>
#include <string>

int main() {
    std::unordered_map<int, std::string> mymap = {{1, "one"}, {2, "two"}, {3, "three"}};
    for (auto& pair : mymap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    return 0;
}

2. 算法(Algorithms)

STL 提供了一系列通用算法,用于对容器中的元素执行各种操作,例如查找、排序、转换等。

  • 查找:std::find, std::find_if, std::binary_search 等。
  • 排序:std::sort, std::stable_sort, std::partial_sort 等。
  • 数值算法:std::accumulate, std::transform, std::inner_product 等。
  • 堆算法:std::make_heap, std::push_heap, std::pop_heap 等。
  • 集合操作:std::merge, std::set_union, std::set_intersection 等。

其中经典的增删改查算法复杂度:

  1. std::vector:

    • 插入(Insertion):
      • 在末尾插入:平均情况下是常数时间复杂度,因为在末尾插入只需要将元素追加到底层数组的末尾,但如果数组大小不够,则需要重新分配内存,将现有元素复制到新内存中,时间复杂度为 O(n)。
      • 在中间或开头插入:需要将插入位置之后的元素向后移动,平均时间复杂度为 O(n)。
    • 查找(Lookup):通过索引直接访问元素,时间复杂度为 O(1)。
    • 删除(Deletion):
      • 在末尾删除:平均情况下是常数时间复杂度,因为只需要移除最后一个元素。
      • 在中间或开头删除:需要将删除位置之后的元素向前移动,平均时间复杂度为 O(n)。
  2. std::deque:

    • 插入(Insertion):
      • 在两端插入:在两端插入操作的时间复杂度为常数,因为 deque 是双端队列,支持快速在两端进行插入操作。
      • 在中间插入:需要对中间位置之后的元素进行移动,平均时间复杂度为 O(n)。
    • 查找(Lookup):通过索引直接访问元素,时间复杂度为 O(1)。
    • 删除(Deletion):
      • 在两端删除:在两端删除操作的时间复杂度为常数。
      • 在中间删除:需要对删除位置之后的元素进行移动,平均时间复杂度为 O(n)。
  3. std::list:

    • 插入(Insertion):在任意位置插入元素的时间复杂度为常数,因为它是双向链表,插入只需要调整指针。
    • 查找(Lookup):需要遍历链表来查找元素,平均时间复杂度为 O(n)。
    • 删除(Deletion):在任意位置删除元素的时间复杂度为常数,因为它是双向链表,删除只需要调整指针。
  4. std::forward_list:

    • 插入(Insertion):在头部插入元素的时间复杂度为常数,因为它是单向链表,插入只需要调整指针。
    • 查找(Lookup):需要遍历链表来查找元素,平均时间复杂度为 O(n)。
    • 删除(Deletion):在任意位置删除元素的时间复杂度为常数,因为它是单向链表,删除只需要调整指针。
  5. std::set:

    • 插入(Insertion):O(log n),其中 n 是集合中元素的数量。插入时需要确保元素的顺序。
    • 查找(Lookup):O(log n),通过红黑树实现,具有对数时间复杂度。
    • 删除(Deletion):O(log n),通过红黑树实现,具有对数时间复杂度。
  6. std::multiset:

    • 插入(Insertion):O(log n),通过红黑树实现,具有对数时间复杂度。
    • 查找(Lookup):O(log n),通过红黑树实现,具有对数时间复杂度。
    • 删除(Deletion):O(log n),通过红黑树实现,具有对数时间复杂度。
  7. std::map:

    • 插入(Insertion):O(log n),通过红黑树实现,具有对数时间复杂度。
    • 查找(Lookup):O(log n),通过红黑树实现,具有对数时间复杂度。
    • 删除(Deletion):O(log n),通过红黑树实现,具有对数时间复杂度。
  8. std::multimap:

    • 插入(Insertion):O(log n),通过红黑树实现,具有对数时间复杂度。
    • 查找(Lookup):O(log n),通过红黑树实现,具有对数时间复杂度。
    • 删除(Deletion):O(log n),通过红黑树实现,具有对数时间复杂度。
  9. std::unordered_set:

    • 插入(Insertion):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是集合中元素的数量。
    • 查找(Lookup):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是集合中元素的数量。
    • 删除(Deletion):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是集合中元素的数量。
  10. std::unordered_multiset:

    • 插入(Insertion):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是多重集合中元素的数量。
    • 查找(Lookup):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是多重集合中元素的数量。
    • 删除(Deletion):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是多重集合中元素的数量。
  11. std::unordered_map:

    • 插入(Insertion):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是映射中元素的数量。
    • 查找(Lookup):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是映射中元素的数量。
    • 删除(Deletion):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是映射中元素的数量。
  12. std::unordered_multimap:

    • 插入(Insertion):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是多重映射中元素的数量。
    • 查找(Lookup):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是多重映射中元素的数量。
    • 删除(Deletion):平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是多重映射中元素的数量。

3. 迭代器(Iterators)

STL(标准模板库)的迭代器是一种泛型的指针,用于遍历容器(如向量、列表、集合等)中的元素,是STL算法和容器之间的桥梁。迭代器提供了一种统一的访问容器元素的方式,使得算法可以独立于容器类型而工作。

迭代器分类:

STL的迭代器可以根据其功能和能力分为不同的类型,主要分为输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。这些迭代器的功能和支持的操作在类型之间有所不同,具体如下:

  1. 输入迭代器(Input Iterator)

    • 支持逐个读取容器中的元素。
    • 只能逐个向前移动,并且只能进行一次遍历。
    • 不能修改容器中的元素。
  2. 输出迭代器(Output Iterator)

    • 支持逐个写入容器中的元素。
    • 只能逐个向前移动,并且只能进行一次遍历。
    • 不能读取容器中的元素。
  3. 前向迭代器(Forward Iterator)

    • 具有输入迭代器的所有功能。
    • 支持多次遍历容器,但只能向前移动。
    • 能够逐个读取和修改容器中的元素。
  4. 双向迭代器(Bidirectional Iterator)

    • 具有前向迭代器的所有功能。
    • 支持向前和向后移动。
    • 通常用于双向链表等数据结构。
  5. 随机访问迭代器(Random Access Iterator)

    • 具有双向迭代器的所有功能。
    • 支持随机访问容器中的元素,可以跳跃式地移动迭代器。
    • 支持比较、加法、减法等操作,具有指针的功能。
迭代器的操作:

迭代器提供了一系列操作来遍历容器中的元素,包括:

  • *it:解引用迭代器,获取迭代器指向的元素。
  • it++++it:后置递增和前置递增,使迭代器指向容器中的下一个元素。
  • it----it:后置递减和前置递减,使迭代器指向容器中的前一个元素。
  • it1 + nit1 - n:随机访问迭代器支持跳跃式移动,使迭代器向前或向后移动 n 个位置。
  • it1 == it2it1 != it2:比较两个迭代器是否相等或不相等。
迭代器的适用范围:

不同类型的迭代器适用于不同类型的容器。例如,随机访问迭代器适用于数组和向量等支持随机访问的容器,而输入迭代器适用于流(例如文件流)等只能顺序访问的容器。选择合适的迭代器类型可以提高算法的效率和灵活性。

总的来说,STL的迭代器是一种强大的工具,为算法和容器提供了一种通用的访问方式,使得用户能够更加灵活地处理数据结构中的元素。

4. 分配器:

分配器是一种用于管理容器内存分配和释放的对象,它定义了容器在创建、销毁、复制和重新分配内存时的行为。STL容器在默认情况下使用标准分配器 std::allocator,但是用户也可以自定义分配器以满足特定的需求,例如性能优化、内存池管理等。

自定义分配器的步骤:
  1. 定义分配器类:自定义一个类,该类要符合 Allocator 的要求,并提供内存分配和释放的操作符。
  2. 在容器声明时指定分配器:在声明容器时,通过模板参数显式指定使用的分配器类型。
#include <iostream>
#include <vector>
#include <memory>

// 自定义分配器类
template<typename T>
class MyAllocator {
public:
    using value_type = T;
    
    MyAllocator() noexcept = default;
    
    template<typename U>
    MyAllocator(const MyAllocator<U>&) noexcept {}
    
    T* allocate(std::size_t n) {
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }
    
    void deallocate(T* ptr, std::size_t) noexcept {
        ::operator delete(ptr);
    }
};

int main() {
    // 使用自定义分配器的 vector
    std::vector<int, MyAllocator<int>> vec;
    vec.push_back(1);
    vec.push_back(2);
    
    for (int num : vec) {
        std::cout << num << " ";
    }
    
    return 0;
}

5. 适配器:

适配器是一种用于修改容器行为的对象,它可以在不修改原始容器的情况下改变其行为或提供新的功能。STL中提供了多种适配器,其中最常见的是容器适配器和迭代器适配器。

容器适配器:

容器适配器是一种修改容器行为的对象,常见的容器适配器包括栈(stack)、队列(queue)和优先队列(priority_queue),它们通过修改底层容器的行为来提供新的功能。

迭代器适配器:

迭代器适配器是一种修改迭代器行为的对象,常见的迭代器适配器包括反向迭代器(reverse_iterator)、插入迭代器(insert_iterator)、流迭代器(stream_iterator)等,它们通过修改迭代器的行为来实现不同的遍历方式或操作方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值