STL容器资料介绍了C++标准模板库中各种容器的特性和使用方法,涵盖了vector、list、deque、set和map等常见类型,并详细讲解了每个容器的基本操作和常用方法。文章还分析了不同容器在插入、删除、访问和查找操作上的时间复杂度,帮助读者根据实际需求选择合适的容器。
STL容器简介STL容器的定义
STL(Standard Template Library,标准模板库)是C++中一个非常重要的库,它提供了一系列的容器类,用来存储、处理、搜索和组织数据。STL容器是一种通用的数据结构,可以容纳不同类型的元素,并提供了一组统一的接口来操作这些数据结构。STL容器分为序列容器(Sequence Container)和关联容器(Associative Container)两大类,每种容器都有其特有的操作方法和适用场景。
STL容器的特点
- 泛型编程:STL容器支持泛型编程,可以在编译时确定类型,因此可以高效地使用。
- 模板化:STL容器通过使用模板技术,可以处理不同类型的数据,具有很高的灵活性。
- 算法配合:STL容器通常与STL算法库配合使用,可以高效地对容器中的数据进行操作。
- 接口一致:所有STL容器都提供了一组通用的接口,使得容器的使用方法和风格一致,方便学习和使用。
常见STL容器类型
- vector:一种动态数组,支持随机访问,底层实现为数组。
- list:双向链表,支持高效的插入和删除操作,但不支持随机访问。
- deque:双端队列,支持高效的插入和删除操作,支持随机访问,但访问中间元素时效率较低。
- set:集合容器,内部元素是唯一且有序的。
- map:映射容器,内部元素是键值对,键是唯一的且有序的。
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;
}
共同学习,写下你的评论
评论加载中...
作者其他优质文章