一、STL概述
1. STL概述
(1)STL定义:STL(standard template library),标准模板库,是一个高效的c++程序库,重在提高了代码的复用性;主要包含了常用的数据结构和基本算法,为广大c++程序员提供了一个可扩展的应用框架;
体现了泛型化(模板化)程序设计的思想,在实现方面是以一种类型程序参数化的方式实现的;采用模板类实现,所以定义时需要执行存储元素的类型(基本类型或自定义类型);
2. STL的六大组件
(1)容器(container):各种基本数据结构
(2)迭代器(iterator):常使用在遍历容器,又可以用在连接容器和算法;
(3)算法(algorithm):提供了各种基本算法如sort、search等;
(4)适配器(adapter):可改变容器(containers)、迭代器(iterators)或函数对象(function object)接口的一种组件;
(5)函数对象(function object):用类型包装函数,将函数类型化;
(6)分配器(allocator):内部调用malloc分配空间;
二、容器
1. 分类:可分为三类:
(1)顺序容器(sequence containers),主要包括array、vector、deque、list、forward_list;
(2)关联容器(associative containers),内部机理是树、哈希表,元素是键/值对,特别适合做查找,因为其时间复杂度较低;且大都具有自动排序功能;主要包括set、multiset、map、multimap;
(3)无序(散列)容器(UNordered containers),内部机理是哈希表,c++11里推出:元素的位置不重要,重要的是这个元素是否在这个容器里面,主要适用于查找;主要包括:underde_set、unordered_multiset、unordered_map、unordered_multimap;
2. vector容器 头文件#include
(1)本质与特点
①vector是内置有动态数组,可以随机存取元素,可用[ ] 和 at()方法;
②vector尾部添加或移除元素非常快速;
③因为vector在中间插入会导致后面的所有元素都要重新析构与拷贝构造,效率很低;
④vector采用模板类实现,所以定义时需要执行存储元素的类型(基本类型或自定义类型)
(2)主要操作
vector对象的默认构造 | vector |
vector对象的带参数构造 | vector |
vector的插入 | push_back、insert |
vector数据存取 | v1.at(), v1[ ], v1.front(), |
vector的删除 | pop_back(), clear(), erase(), |
vector的迭代器 | begin(), end(), rbegin(), rend() |
vector的大小 | size(), empty(), capacity(), resize(), shrink_to_fit |
vector的赋值 | assign(), operator=, swap |
vector示例:
#include
#include
using namespace std;
class Test
{
public:
Test()
{
// cout << "Test" << endl;
}
Test(int t) : m_t(t)
{
// cout << "Test int" << endl;
}
Test(const Test &other)
{
m_t = other.m_t;
// cout << "Test copy" << endl;
}
~Test()
{
// cout << "~Test" << endl;
}
friend ostream& operator<<(ostream&out, Test &t);
friend bool operator!=(const Test&t, int num);
friend int operator%(const Test&t, int num);
int m_t;
};
ostream& operator<<(ostream&out, Test &t)
{
out<
void print(vector &vt)
{
for(int i = 0; i < vt.size(); i++)
{
cout<
void delete_node(vector &vt)
{
for(auto it = vt.begin(); it != vt.end(); )
{
if(*it % 2 != 0)
{
// it = vt.erase(it);
vt.erase(it);
}
else
{
++it;
}
}
}
int main(int argc, char **argv)
{
vector vt;
vt.reserve(20); //提前预留20个元素值,避免了空间不够时,频繁的拷贝构造
for(int i = 0; i < 9; i++)
{
vt.push_back(Test(i+1)); //连续构造1-10
}
print(vt);
delete_node(vt); //删除其中是基数的元素值(test的m_t是奇数的)
print(vt);
cout<<"vt.capacity() = "<
3. list 容器 头文件 #include
(1)本质及特点
①list是一个双向链表容器,适合高效地在头尾插入或者删除元素;
②list不支持随机存取元素,所以不支持at()方式与[ ]操作符;请注意迭代器方面it++可以,但是it+2这样就不支持了;只可遍历,不可跳跃;
(2)主要操作
list对象的默认构造 | list |
list对象的带参数构造 | list |
list的插入 | l2.push_back(); l2.pop_back(); l2.push_front(); l2.pop_front(); l2.emplace_back(); l2.emplace_front(); |
list元素删除 | l2.clear(); l2.erase(beg,end); l2.erase(pos); l2.remove(elem); l2.remove_if(); list.unique(); |
list的迭代器 | l2.begin(); l2.end(); l2.rbegin(); l2.rend(); |
list的元素存取 | l2.front(); l2.back(); |
list的赋值 | l2.assign(); l2.swap(l1); |
list的大小 | l2.size(); l2.empty(); l2.resiae(); l2.resize(); |
list的排列 | l2.sort(); l2.reverse(); |
list对容器的操作 | splice(); |
list示例:
#include
#include
#include
using namespace std;
template
class Deleteor
{
public:
bool operator()(const T &t)
{
return (t % 2) == 0;
}
};
template
void print(list & other)
{
for(auto it = other.begin(); it != other.end(); ++it)
{
cout<<*it<<" ";
}
cout< l1;
l1.emplace_back(1);
l1.emplace_back(2);
l1.emplace_back(3);
l1.emplace_back(4);
l1.emplace_back(5);
l1.emplace_back(6);
l1.emplace_back(7);
l1.emplace_front(8);
print(l1);
l1.insert(std::next(l1.begin(),3),9); //std::next(l1.begin(),3)是制定插入的位置
print(l1);
l1.remove(4); //删除值等于4的元素
print(l1);
l1.remove_if(Deleteor()); //Deleteor()是删除的规则,返回的是bool(true,false)
print(l1); //这里是一个函数对象,重载了函数运算符()
l1.sort(); //对list排序,默认是从小到大
print(l1);
l1.sort(greater()); //内建函数(对象),实现从大到小排序
print(l1);
l1.reverse(); //对list逆序
print(l1);
listl2 = {1,3,5,7,9};
listl3 = {2,4,6,8,10};
l2.merge(l3); //将l3合并到l2上,并按照默认的从小到大方式排好序
cout<<"l2 : ";
print(l2);
cout<<"l3 : ";
print(l3); //l3会被清空
listl4 = {7,7,7,7};
auto it = l2.begin();
std::advance(it,2); //将it迭代器后退2个位置,-2就是前进2个位置
l2.splice(it,l4);
cout<<"l2 : ";
print(l2);
return 0;
}
4. forward_list容器
本质及特点:优化list产生的,forward_list是单向链表,这样的话少存储一个指针,节省系统开销;
①只提供前向迭代器,不支持反向迭代器;
②不提供size()成员函数;
③没有指向最末元素的锚点,因此不提供back(),push_back(),pop_back();
④不提供随机访问,与list同;
⑤插入和删除元素不会造成“指向其他元素的”指针、引用、迭代器失效;
5. deque 容器,本质及特点:
①deque是双端数组,而vector是单端的;
②与vector一样,可以随机存取元素;且在头部和尾部移除元素都非常快速,但是在中间插入元素则比较费时;
③其他内容以vector一样;
6. set/multiset 容器 头文件:#include
(1)本质及特点
①set是一个集合容器,其中包含的元素是唯一的,集合中的元素按一定的顺序排列;元素插入过程是按排序规则插入,所以不能指定插入位置;
②set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树,在插入操作和删除操作上比vector快;时间复杂度是O(log n);
③set不可以直接存取元素(不可以使用at.(pos)与[ ] 操作符);
④multiset与set的区别是:set支持唯一键值,即每一个元素只能出现一次;而multiset中的元素可以出现多次;
⑤不可以直接修改set或multiset容器中的元素值,因为这类容器是自动排序的,如果要修改其中的某个元素值,必须先删除原有的元素,再插入新的元素;
程序示例:
#include
#include
#include
using namespace std;
template
void print(const set &other)
{
for(auto it = other.begin(); it != other.end(); ++it)
{
cout<<*it<<" ";
}
cout<si;
si.insert(1);
si.insert(3);
si.insert(7);
si.insert(5);
si.insert(2);
print(si);
// auto node = si.extract(3); //修改成员值,c++17,本系统不支持
// node.value = 9;
// si.insert(move(node));
// sets2 = {2,4,6,8};
// si.merge(s2); //将s2合并到si,c++17,本系统不支持
cout<<"si.count(5) : "<
7. map/multimap 容器 #include
(1)本质及特点
①map是标准的关联式容器,一个map是一个键值对(一个序列号,一个值,相当于数组的索引和对应的值),它提供基于key(键值)的快速检索能力;键值对中第一个元素是first,第二个元素是second,支持迭代器访问输出;
②map中的key值是唯一的;集合中的元素按一定的顺序排列,元素插入过程是按排序规则插入,所以不能指定插入位置;
③map的具体实现采用红黑树变体的平衡二叉树数据结构,在插入和删除操作上比vector块;
④map可以直接存取key所对应的value,支持[ ] 操作符,如map[key] = value;
⑤multimap 与 map 的区别:map支持唯一键值,每个键只能出现一次,而multimap中相同的键可以出现多次。但是multimap不支持[ ]操作符;
(2)示例
#include
#include
(3)一个面试题示例:给定一个数组,计算每个数值出现的次数,然后按字符出现次数的升序输出数值及其出现次数;并且单独输出个数大于数组个数一半的元素;
#include
#include
结果:
8. 容器的选择原则总结
①vector是内置有动态数组,当容量满时会自动扩充,在末尾有定位指针,所以末尾插入十分快速,但中间插入则较慢,因为涉及到原有数组的拷贝;最大的特点是适合高效的随机访问;而deque则优化为可以既可以头插也可以末尾插;
②list是一个双向链表容器,访问指定元素的效率较低,不支持随机访问,最大的特点是中间插入的效率较快;
③set是一个集合容器,元素具有唯一性,因为排序功能,所以开销较大;若需要支持重复数据则需要使用multiset;
④map是关联式容器,内置键值对,且内部按照顺序排序,内部采用红黑树的数据结构,时间复杂度是O(log n ),查找与删除的效率很高;适合字典式存储数据;