为了账号安全,请及时绑定邮箱和手机立即绑定

STL容器资料入门教程

概述

STL容器资料介绍了C++标准模板库中各种容器的特性和使用方法,涵盖了vector、list、deque、set和map等常见类型,并详细讲解了每个容器的基本操作和常用方法。文章还分析了不同容器在插入、删除、访问和查找操作上的时间复杂度,帮助读者根据实际需求选择合适的容器。

STL容器简介

STL容器的定义

STL(Standard Template Library,标准模板库)是C++中一个非常重要的库,它提供了一系列的容器类,用来存储、处理、搜索和组织数据。STL容器是一种通用的数据结构,可以容纳不同类型的元素,并提供了一组统一的接口来操作这些数据结构。STL容器分为序列容器(Sequence Container)和关联容器(Associative Container)两大类,每种容器都有其特有的操作方法和适用场景。

STL容器的特点

  1. 泛型编程:STL容器支持泛型编程,可以在编译时确定类型,因此可以高效地使用。
  2. 模板化:STL容器通过使用模板技术,可以处理不同类型的数据,具有很高的灵活性。
  3. 算法配合:STL容器通常与STL算法库配合使用,可以高效地对容器中的数据进行操作。
  4. 接口一致:所有STL容器都提供了一组通用的接口,使得容器的使用方法和风格一致,方便学习和使用。

常见STL容器类型

  1. vector:一种动态数组,支持随机访问,底层实现为数组。
  2. list:双向链表,支持高效的插入和删除操作,但不支持随机访问。
  3. deque:双端队列,支持高效的插入和删除操作,支持随机访问,但访问中间元素时效率较低。
  4. set:集合容器,内部元素是唯一且有序的。
  5. map:映射容器,内部元素是键值对,键是唯一的且有序的。
vector容器详解

vector容器的基本操作

vector是一种动态数组,它可以按需动态增加或减少存储的元素数量。vector容器通常用于需要随机访问的数据结构中。vector容器的基本操作包括:

  • 构造函数:创建vector容器。
  • 下标操作:通过下标访问vector中的元素。
  • push_back:向容器末尾添加元素。
  • pop_back:删除容器末尾的元素。
  • size:返回容器中元素的数量。
  • empty:判断容器是否为空。
  • clear:清空容器中的所有元素。

示例如下:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec; // 使用默认构造函数创建vector容器

    vec.push_back(1); // 向容器末尾添加元素
    vec.push_back(2);
    vec.push_back(3);

    std::cout << "vec[0] = " << vec[0] << std::endl; // 通过下标访问元素
    std::cout << "vec[1] = " << vec[1] << std::endl;

    std::cout << "Size of vec: " << vec.size() << std::endl; // 访问容器的大小
    std::cout << "vec is empty? " << vec.empty() << std::endl; // 判断容器是否为空

    vec.pop_back(); // 删除末尾元素
    vec.clear(); // 清空容器中的所有元素
    std::cout << "Size of vec after clear: " << vec.size() << std::endl;

    return 0;
}

vector容器的常用方法

vector容器提供了许多常用的方法,这些方法可以方便地对容器进行操作。以下是一些常用的vector方法:

  • begin:返回指向容器第一个元素的迭代器。
  • end:返回指向容器最后一个元素后面位置的迭代器。
  • front:返回容器的第一个元素。
  • back:返回容器的最后一个元素。
  • insert:在指定位置插入元素。
  • erase:删除指定位置的元素。
  • resize:修改容器的大小。

示例如下:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec;

    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);

    std::cout << "First element: " << vec.front() << std::endl; // 访问第一个元素
    std::cout << "Last element: " << vec.back() << std::endl; // 访问最后一个元素

    vec.insert(vec.begin() + 1, 10); // 在第二个位置插入元素10
    std::cout << "After insert: " << vec[1] << std::endl;

    vec.erase(vec.begin() + 1); // 删除第二个位置的元素
    std::cout << "After erase: " << vec[1] << std::endl;

    vec.resize(5); // 将容器大小调整为5
    std::cout << "Size after resize: " << vec.size() << std::endl;

    return 0;
}

vector容器的使用示例

考虑一个简单的例子,使用vector容器存储学生的成绩,并对成绩进行操作:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> scores;

    // 添加成绩
    scores.push_back(85);
    scores.push_back(90);
    scores.push_back(78);
    scores.push_back(92);

    // 输出成绩
    std::cout << "Scores: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    // 修改第一个成绩
    scores[0] = 88;
    std::cout << "Modified scores: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    // 删除最后一个成绩
    scores.pop_back();
    std::cout << "After pop back: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    return 0;
}
list容器详解

list容器的基本操作

list容器是一种双向链表,每个元素都有前驱和后继指针。list容器支持高效的插入和删除操作,但不支持随机访问。list容器的基本操作包括:

  • 构造函数:创建list容器。
  • 下标操作:通过下标访问list中的元素(不支持随机访问)。
  • push_back:向容器末尾添加元素。
  • push_front:向容器头部添加元素。
  • pop_back:删除容器末尾的元素。
  • pop_front:删除容器头部的元素。
  • size:返回容器中元素的数量。
  • empty:判断容器是否为空。
  • clear:清空容器中的所有元素。

示例如下:

#include <list>
#include <iostream>

int main() {
    std::list<int> lst; // 使用默认构造函数创建list容器

    lst.push_back(1); // 向容器末尾添加元素
    lst.push_back(2);
    lst.push_back(3);

    std::cout << "First element: " << *lst.begin() << std::endl; // 访问第一个元素
    std::cout << "Last element: " << *lst.rbegin() << std::endl; // 访问最后一个元素

    std::cout << "Size of lst: " << lst.size() << std::endl; // 访问容器的大小
    std::cout << "lst is empty? " << lst.empty() << std::endl; // 判断容器是否为空

    lst.pop_back(); // 删除末尾元素
    lst.pop_front(); // 删除头部元素
    lst.clear(); // 清空容器中的所有元素
    std::cout << "Size of lst after clear: " << lst.size() << std::endl;

    return 0;
}

list容器的常用方法

list容器提供了许多常用的方法,这些方法可以方便地对容器进行操作。以下是一些常用的list方法:

  • begin:返回指向容器第一个元素的迭代器。
  • end:返回指向容器最后一个元素后面位置的迭代器。
  • front:返回容器的第一个元素。
  • back:返回容器的最后一个元素。
  • insert:在指定位置插入元素。
  • erase:删除指定位置的元素。
  • splice:拼接两个list容器。

示例如下:

#include <list>
#include <iostream>

int main() {
    std::list<int> lst;

    lst.push_back(1);
    lst.push_back(2);
    lst.push_back(3);

    std::cout << "First element: " << lst.front() << std::endl; // 访问第一个元素
    std::cout << "Last element: " << lst.back() << std::endl; // 访问最后一个元素

    lst.insert(lst.begin() + 1, 10); // 在第二个位置插入元素10
    std::cout << "After insert: " << *lst.begin() << " " << *(lst.begin() + 1) << " " << *(lst.begin() + 2) << std::endl;

    lst.erase(lst.begin() + 1); // 删除第二个位置的元素
    std::cout << "After erase: " << *lst.begin() << " " << *(lst.begin() + 1) << " " << *(lst.begin() + 2) << std::endl;

    std::list<int> lst2;
    lst2.push_back(4);
    lst2.push_back(5);

    lst.splice(lst.begin() + 1, lst2); // 将lst2拼接到lst的第二个位置
    std::cout << "After splice: ";
    for (int num : lst) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

list容器的使用示例

考虑一个简单的例子,使用list容器存储学生的成绩,并对成绩进行操作:

#include <list>
#include <iostream>

int main() {
    std::list<int> scores;

    // 添加成绩
    scores.push_back(85);
    scores.push_back(90);
    scores.push_back(78);
    scores.push_back(92);

    // 输出成绩
    std::cout << "Scores: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    // 修改第一个成绩
    scores.front() = 88;
    std::cout << "Modified scores: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    // 删除最后一个成绩
    scores.pop_back();
    std::cout << "After pop back: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    return 0;
}
deque容器详解

deque容器的基本操作

deque容器是一种双端队列,它支持高效的插入和删除操作,同时支持随机访问。deque容器的基本操作包括:

  • 构造函数:创建deque容器。
  • 下标操作:通过下标访问deque中的元素。
  • push_back:向容器末尾添加元素。
  • push_front:向容器头部添加元素。
  • pop_back:删除容器末尾的元素。
  • pop_front:删除容器头部的元素。
  • size:返回容器中元素的数量。
  • empty:判断容器是否为空。
  • clear:清空容器中的所有元素。

示例如下:

#include <deque>
#include <iostream>

int main() {
    std::deque<int> deq; // 使用默认构造函数创建deque容器

    deq.push_back(1); // 向容器末尾添加元素
    deq.push_back(2);
    deq.push_back(3);

    std::cout << "First element: " << deq[0] << std::endl; // 访问第一个元素
    std::cout << "Last element: " << deq[deq.size() - 1] << std::endl; // 访问最后一个元素

    std::cout << "Size of deq: " << deq.size() << std::endl; // 访问容器的大小
    std::cout << "deq is empty? " << deq.empty() << std::endl; // 判断容器是否为空

    deq.pop_back(); // 删除末尾元素
    deq.pop_front(); // 删除头部元素
    deq.clear(); // 清空容器中的所有元素
    std::cout << "Size of deq after clear: " << deq.size() << std::endl;

    return 0;
}

deque容器的常用方法

deque容器提供了许多常用的方法,这些方法可以方便地对容器进行操作。以下是一些常用的deque方法:

  • begin:返回指向容器第一个元素的迭代器。
  • end:返回指向容器最后一个元素后面位置的迭代器。
  • front:返回容器的第一个元素。
  • back:返回容器的最后一个元素。
  • insert:在指定位置插入元素。
  • erase:删除指定位置的元素。
  • resize:修改容器的大小。

示例如下:

#include <deque>
#include <iostream>

int main() {
    std::deque<int> deq;

    deq.push_back(1);
    deq.push_back(2);
    deq.push_back(3);

    std::cout << "First element: " << deq.front() << std::endl; // 访问第一个元素
    std::cout << "Last element: " << deq.back() << std::endl; // 访问最后一个元素

    deq.insert(deq.begin() + 1, 10); // 在第二个位置插入元素10
    std::cout << "After insert: " << deq[0] << " " << deq[1] << " " << deq[2] << std::endl;

    deq.erase(deq.begin() + 1); // 删除第二个位置的元素
    std::cout << "After erase: " << deq[0] << " " << deq[1] << " " << deq[2] << std::endl;

    deq.resize(5); // 将容器大小调整为5
    std::cout << "Size after resize: " << deq.size() << std::endl;

    return 0;
}

deque容器的使用示例

考虑一个简单的例子,使用deque容器存储学生的成绩,并对成绩进行操作:

#include <deque>
#include <iostream>

int main() {
    std::deque<int> scores;

    // 添加成绩
    scores.push_back(85);
    scores.push_back(90);
    scores.push_back(78);
    scores.push_back(92);

    // 输出成绩
    std::cout << "Scores: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    // 修改第一个成绩
    scores.front() = 88;
    std::cout << "Modified scores: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    // 删除最后一个成绩
    scores.pop_back();
    std::cout << "After pop back: ";
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    return 0;
}
set和map容器简述

set容器的使用方法

set容器是一种集合容器,它内部存储的元素是唯一的且有序的。set容器的基本操作包括:

  • 构造函数:创建set容器。
  • 插入元素:使用insert方法插入元素。
  • 查找元素:使用find方法查找元素。
  • 删除元素:使用erase方法删除元素。
  • 遍历容器:使用迭代器遍历容器中的元素。

示例如下:

#include <set>
#include <iostream>

int main() {
    std::set<int> s; // 使用默认构造函数创建set容器

    s.insert(3); // 插入元素
    s.insert(1);
    s.insert(2);

    std::cout << "Is 2 in s? " << (s.find(2) != s.end() ? "Yes" : "No") << std::endl; // 查找元素

    s.erase(s.find(2)); // 删除元素
    std::cout << "Size of s after erase: " << s.size() << std::endl; // 访问容器的大小

    std::cout << "Set: ";
    for (int num : s) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

map容器的使用方法

map容器是一种映射容器,它内部存储的是键值对,键是唯一的且有序的。map容器的基本操作包括:

  • 构造函数:创建map容器。
  • 插入元素:使用insert方法插入元素。
  • 查找元素:使用find方法查找元素。
  • 删除元素:使用erase方法删除元素。
  • 遍历容器:使用迭代器遍历容器中的元素。

示例如下:

#include <map>
#include <iostream>

int main() {
    std::map<int, std::string> m; // 使用默认构造函数创建map容器

    m.insert(std::make_pair(1, "Alice")); // 插入元素
    m.insert(std::make_pair(2, "Bob"));
    m.insert(std::make_pair(3, "Charlie"));

    std::cout << "Name of key 2: " << m.find(2)->second << std::endl; // 查找元素

    m.erase(m.find(2)); // 删除元素
    std::cout << "Size of m after erase: " << m.size() << std::endl; // 访问容器的大小

    std::cout << "Map: ";
    for (auto& pair : m) {
        std::cout << pair.first << ": " << pair.second << " ";
    }
    std::cout << std::endl;

    return 0;
}
``

### set和map容器的区别与联系

1. **区别**:
   - set容器存储的是唯一的元素,而map容器存储的是键值对,键是唯一的。
   - set容器中的元素是无序的,而map容器中的元素是根据键进行排序的。

2. **联系**:
   - set和map容器都支持高效的插入、删除和查找操作。
   - set和map容器都提供了`find`、`insert`、`erase`等常用方法。

### set容器的使用示例

```cpp
#include <set>
#include <iostream>

int main() {
    std::set<int> s;

    s.insert(3);
    s.insert(1);
    s.insert(2);

    if (s.find(2) != s.end()) {
        std::cout << "2 is in set." << std::endl;
    } else {
        std::cout << "2 is not in set." << std::endl;
    }

    s.erase(s.find(2));

    std::cout << "Set size: " << s.size() << std::endl;

    std::cout << "Set elements: ";
    for (int num : s) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

map容器的使用示例

#include <map>
#include <iostream>

int main() {
    std::map<int, std::string> m;

    m.insert(std::make_pair(1, "Alice"));
    m.insert(std::make_pair(2, "Bob"));
    m.insert(std::make_pair(3, "Charlie"));

    if (m.find(2) != m.end()) {
        std::cout << "Name of key 2: " << m.find(2)->second << std::endl;
    }

    m.erase(m.find(2));

    std::cout << "Map size: " << m.size() << std::endl;

    std::cout << "Map elements: ";
    for (auto& pair : m) {
        std::cout << pair.first << ": " << pair.second << " ";
    }
    std::cout << std::endl;

    return 0;
}
STL容器的性能分析

不同容器的时间复杂度

不同类型的容器在不同的操作上会有不同的时间复杂度。以下是一些常见容器的时间复杂度:

  • vector
    • 插入/删除:在容器末尾插入/删除元素的时间复杂度为O(1),在容器中间插入/删除元素的时间复杂度为O(n)。
    • 访问:访问元素的时间复杂度为O(1)。
    • 查找:查找元素的时间复杂度为O(n)。
  • list
    • 插入/删除:插入/删除元素的时间复杂度为O(1)。
    • 访问:访问元素的时间复杂度为O(n)。
    • 查找:查找元素的时间复杂度为O(n)。
  • deque
    • 插入/删除:在容器头部或末尾插入/删除元素的时间复杂度为O(1),在容器中间插入/删除元素的时间复杂度为O(n)。
    • 访问:访问元素的时间复杂度为O(1)。
    • 查找:查找元素的时间复杂度为O(n)。
  • set/map
    • 插入/删除:插入/删除元素的时间复杂度为O(log n)。
    • 访问:访问元素的时间复杂度为O(log n)。
    • 查找:查找元素的时间复杂度为O(log n)。

容器的选择依据

选择容器时,需要根据实际应用场景的需求来决定。以下是一些选择依据:

  • 插入/删除操作的频繁程度:如果插入/删除操作频繁,应该选择list或deque容器。
  • 访问操作的频繁程度:如果访问操作频繁,应该选择vector或deque容器。
  • 查找操作的频繁程度:如果查找操作频繁,应该选择set或map容器。
  • 是否需要元素有序:如果需要元素有序,应该选择set或map容器。
  • 是否需要元素唯一:如果需要元素唯一,应该选择set或map容器。

容器的内存使用情况

容器在内存中的使用情况也会影响选择。以下是一些内存使用情况的考虑:

  • vector:vector容器的内存使用量固定,可以在编译时确定。
  • list:list容器的内存使用量随着元素的增加而线性增长。
  • deque:deque容器的内存使用量随着元素的增加而线性增长,但比list容器更高效。
  • set/map:set和map容器的内存使用量随着元素的增加而增加,但比vector和deque容器更高效。

性能分析代码示例

#include <vector>
#include <list>
#include <set>
#include <map>
#include <deque>
#include <iostream>
#include <chrono>

void measureInsertTime(std::vector<int>& vec, std::list<int>& lst, std::deque<int>& deq, std::set<int>& s, std::map<int, int>& m) {
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; ++i) {
        vec.push_back(i);
        lst.push_back(i);
        deq.push_back(i);
        s.insert(i);
        m.insert(std::make_pair(i, i));
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "Insert time: " << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << "us" << std::endl;
}

void measureEraseTime(std::vector<int>& vec, std::list<int>& lst, std::deque<int>& deq, std::set<int>& s, std::map<int, int>& m) {
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; ++i) {
        vec.erase(vec.begin() + i % vec.size());
        lst.erase(lst.begin() + i % lst.size());
        deq.erase(deq.begin() + i % deq.size());
        s.erase(s.begin());
        m.erase(m.begin());
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "Erase time: " << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << "us" << std::endl;
}

void measureAccessTime(std::vector<int>& vec, std::list<int>& lst, std::deque<int>& deq, std::set<int>& s, std::map<int, int>& m) {
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; ++i) {
        vec[i];
        lst.front();
        deq[i];
        s.find(i);
        m.find(i);
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "Access time: " << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << "us" << std::endl;
}

int main() {
    std::vector<int> vec;
    std::list<int> lst;
    std::deque<int> deq;
    std::set<int> s;
    std::map<int, int> m;

    measureInsertTime(vec, lst, deq, s, m);
    measureEraseTime(vec, lst, deq, s, m);
    measureAccessTime(vec, lst, deq, s, m);

    return 0;
}
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消