【C++】 STL详解
若有错误,请多指正!
C++ 中的 STL(Standard Template Library),即 标准模板库,是 C++ 标准库中的一个组成部分。它提供了一套丰富的模板类、函数和算法,用于简化常用数据结构和算法的实现,提高代码的复用性和可维护性。
STL 包括三个主要组件:
容器(Containers):提供了几种不同类型的容器,例如 vector、list、map、set、queue 和 stack 等,每种容器都有其特定的功能和性能特征,以满足不同的需要。
迭代器(Iterators):提供了一套通用的迭代器接口,它们可以遍历各种容器中的元素,为算法实现提供统一的接口。
算法(Algorithms):提供了一系列通用的算法,例如排序、查找、计数、变换等。这些算法可以适用于各种容器类型,并具有良好的性能和可读性。
STL 的优点在于它提供了一套通用、高效、安全、标准的工具集,使得程序员可以更加容易地实现常用的数据结构和算法,同时也可以编写出安全、易读、高效的代码,从而降低了开发和维护的难度。
C++ STL 中所有的容器类如下:
C++ STL 中的迭代器可以被用于遍历容器中的元素,它是一种抽象概念,可以遍历各种STL容器。以下是 C++ STL 中所有的迭代器:
输入迭代器(Input iterator):输入迭代器只支持单向移动,可以读取容器中的元素。例如,istream_iterator 就是一种输入迭代器,用于从文件或输入流中读取数据。
输出迭代器(Output iterator):输出迭代器也只支持单向移动,可以将数据写入容器中。例如,ostream_iterator 就是一种输出迭代器,可以将数据写入文件或输出流中。
前向迭代器(Forward iterator):前向迭代器支持单向移动,可以读取和修改容器中的元素,但不能反复遍历同一个元素。例如,list 和 forward_list 都支持前向迭代器。
双向迭代器(Bidirectional iterator):双向迭代器支持双向移动,可以在容器中向前或向后遍历元素。例如,deque、set、multiset、map、multimap 等支持双向迭代器。
随机访问迭代器(Random access iterator):随机访问迭代器支持任意方向上的移动,可以直接访问容器中的任意元素,支持常数时间的加、减和随机访问操作。例如,vector、string 和 array 都支持随机访问迭代器。
⭐除了以上五种迭代器,C++ STL 还提供了一些其他类型的迭代器,例如反向迭代器(reverse iterator)、插入迭代器(insert iterator)等。
总的来说,C++ STL 中的迭代器是用于遍历容器中元素的标准化接口,它们可以方便地在各种容器上进行操作,并且提供了多种不同的遍历方式。
C++ STL 中的数学函数主要包括以下几个:
除此之外,STL 还提供了一些其他的数学函数,例如:
总的来说,C++ STL 中提供了许多数学函数和算法函数,它们可以大大简化程序员的开发工作,提高代码的可维护性和可读性。
std::vector vec {1, 2, 3, 4, 5};
auto it = std::find(vec.begin(), vec.end(), 3);
if (it != vec.end()) {
std::cout << "Found: " << *it << '\n';
}
std::vector vec {1, 2, 3, 4, 5, 3};
int count = std::count(vec.begin(), vec.end(), 3);
std::cout << "Count of 3: " << count << '\n';
std::vector src {1, 2, 3, 4, 5};
std::vector dest(5); // 目标容器需要预分配空间
std::copy(src.begin(), src.end(), dest.begin());
for (auto i : dest) {
std::cout << i << ' ';
}
std::vector vec {1, 2, 3, 4, 5};
std::vector dest(vec.size());
std::transform(vec.begin(), vec.end(), dest.begin(),
[](int i) { return i * 2; });
for (auto i : dest) {
std::cout << i << ' ';
}
std::vector vec {5, 2, 4, 1, 3};
std::sort(vec.begin(), vec.end()); // 默认从小到大排序
for (auto i : vec) {
std::cout << i << ' ';
}
可以和 STL 容器、算法、迭代器等紧密结合使用,可以方便地推导出容器中元素、算法返回值、迭代器指向的类型等信息。例如,在使用 STL 容器时,可以使用 auto 定义迭代器类型:
std::vector vec {1, 2, 3, 4, 5};
for(auto it = vec.begin(); it != vec.end(); ++it) {
// 使用 auto 推导出迭代器类型为 decltype(vec)::iterator
std::cout << *it << " ";
}
在 C++11 之前,如果要使用 STL 容器的迭代器,需要编写冗长的迭代器类型名称,例如:
std::vector vec {1, 2, 3, 4, 5};
for(std::vector::const_iterator it = vec.begin(); it != vec.end(); ++it) {
// 使用 std::vector::const_iterator 进行迭代
std::cout << *it << " ";
}
通过使用 auto 关键字,可以让代码更简洁、易读,降低出错的概率。因此,虽然 auto 不属于 STL,但在使用 STL 时经常会和 auto 关键字一起使用。
一些常见用法
int a[5] = {1, 3, 5, 7, 9};
for (auto x : a) cout << x;
cout << endl;
vectorv(7, 6);
for (auto x : v) cout << x;
cout << endl;
string s = "acdb";
for (auto x : s) cout << x;
cout << endl;
char c[5] = {'b', 'a', 'd', 'c'};
for (auto x : c) cout << x;
// 输出
13579
6666666
acdb
badc
str=str.replace(str.find("a"),2,"#"); //从第一个a位置开始的两个字符替换成#
str=str.replace(str.begin(),str.begin()+5,"#"); //用#替换从begin位置开始的5个字符
c=s.substr(a,b)//截取s的下标a开始的b的字符(不足或b不写则到结尾)并赋值给c
string s = "asdfg";
cout << s.substr(1,2) << endl;//此时会输出sd
string s1="asd", s2="asd";
s1.compare(s2);
/*相等返回0, 大于返回1, 小于返回-1*/
char s1[10]="asd", s2[10]="zxc"
strcmp(s1, s2);
/*相等返回0*/
string str="asdf";
reverse(str.begin(),str.end());
string型:s.length(), s.size()
char型:strlen(s)
sizeof(str)/sizeof(str[0]),s.length()
strlen()函数求出的字符串长度为有效长度,既不包含字符串末尾结束符 ‘\0’;
sizeof()操作符求出的长度包含字符串末尾的结束符 ‘\0’;
length()函数求出的字符串长度不包含字符串末尾结束符’\0’;
当在函数内部使用sizeof()求解由函数的形参传入的字符数组的长度时,得到的结果为指针的长度,既对应变量的字节数,而不是字符串的长度,此处一定要小心。
string str;
getline(cin,str);
char ch[20];
cin.get(ch, 11);//获取10个字符
char ch1;
cin.get(ch1);//获取1个字符,相当于ch1 = cin.get()
char a[20];
cin.getline(a, 20);
char m[3][20];
for(int i=0;i<3;i++)
cin.getline(m[i],20);
char ch[20];
gets(ch);
(lower_bound(a,a+n,int num)-a):在数组a中找到第一个大于等于num的数字并返回其下标
int a[] = {1, 2, 3, 4, 5, 7, 8, 9};
printf("%d", lower_bound(a, a + 8, 6) - a);//-a不能少,因为是地址相减
输出5,a->a+8中第一个大于等于6的位置是5
(upper_bound(a,a+n,int num)-a):在数组a中找到第一个大于num的数字并返回其下标
bool compare(int a,int b){
return a>b;//降序
}
sort(num,num+4,compare);
int num[6]={1,0,5,2,5,0};
cout << *max_element(num,num+6) << endl;
//则此时输出的是num数组的最大值5
cout << max_element(num,num+6)-num << endl;
//则此时输出的是num数组的最大值的下标2
unique(data,data+n)//将date数组去重,n是数组长度
swap(a,b)//a和b进行交换
int num[3]={ 1, 2, 3 };
reverse( num, num+3 );//3是数组长度
vector v = { 1, 2, 3 };
reverse(v.begin(),v.end());
string str="asdf";
reverse(str.begin(),str.end());
int num[] = {1, 2, 3};
int *index;
index = find(num, num+3, 3);//index是查找到3的所在地址
cout << index-num << endl;//用3所在地址减去数组首元素地址就是3的下标
index = find(num, num+3, 5);//找不到返回数组长度所在地址
cout << index-num << endl;
/*这里输出数组长度3*/
__gcd()
辗转相除(最大公因数、最小公倍数)
求最大公约数,调用万能头文件
int gc = __gcd(4,8);
//如果用不了参考
int gcd(int a,int b){
if(b==0) return a;
else
return gcd(b,a%b);
}
最小公倍数
int lcm(int a,int b){
return a/__gcd(a,b)*b;
}
//int型数组:
sort(num,num+3);
int num[3] = {5,2,6};
sort(num,num+3);
//先使用sort排个序
do{
for(int i=0;i<3;i++)
cout << num[i] << " ";
cout << endl;
}while(next_permutation(num,num+3));
//string型:
string s;
cin >> s ;
sort(s.begin(),s.end());
//先使用sort排个序
do{
cout << s << endl;
}while(next_permutation(s.begin(),s.end()));
vector、stack、queue、map、pair、heap
向量(vector)、双端队列(deque)、链表(list)、集合(set)、多重集合(multiset)、映射(map)和多重映射(multimap)
一些函数用法还没记全
循环时用 auto 或者 迭代器 iterator
一种容器,类似动态数组,但它的size可以动态改变
vector在获取元素和对最后一个元素的操作效率上更高;但对于中间元素的操作,性能则相对较差。
vectorv
vectorb(a) //将a中的元素复制到b中
vectora(100,6)//定义100个值为6的元素
vectorb(a.begin(),a.end()) //将动态数组a的元素值复制到b中
v.begin()//返回指向迭代器第一个元素的指针
v.end()//返回指向迭代器最后一个元素的指针
v.size()//返回当前vector使用数据量的大小
v.empty()//测试vector是否为空
v.front()//返回第一个元素的值
v.back()//返回最后一个元素的值
v.push_back(x)//在容器最后一个位置插入元素x
v.pop_back(x)//删除最后一个元素
//insert erase swap
v.clear()//清空
//Iterators迭代器
vector::iterator iter1;
for (iter1 = v.begin(); iter1 != v.end(); ++iter1) {
cout << ' ' << *iter1 << endl; //[0,1,2,3,4,5,6,7,8,9]
}
queue q:建立一个队列q,其内部元素类型是int。
q.push(a):将元素a插入到队列q的末尾。
q.pop():删除队列q的队首元素。
q.front():查询q的队首元素。
q.back():查询q的队尾元素。
q.size():查询q的元素个数。
q.empty():查询q是否为空。
seta;
a.insert()//往集合中添加元素,它的参数也是只有一个,就是你想要添加的元素,无返回值。
a.count()//查找出现在集合中的元素的个数,返回值要么是1要么是0
//因为集合的互异性,所以它的返回值要么是1要么是0。
//一般也可以用它来判断某个元素是否在该集合中。
a.begin()和a.end()
//是有返回值的,返回值分别是第一个指向该元素的迭代器和指向最后一个元素迭代器的下一个位置
a.erase()//函数是用来删除指定的元素,参数只有一个,并且是你想要删除的元素,无返回值
a.size()//得出集合中的元素个数。
a.empty()//判断该集合是否为空。
a.clear()//清空集合中的元素。
stack S; //定义一个存储整数类型的栈
top()//返回一个栈顶元素的引用,类型为 T&。如果栈为空,返回值未定义。
push(const T& obj)//将对象压入栈顶
//这是通过调用底层容器的 push_back() 函数完成的。
push(T&& obj)//以移动对象的方式将对象压入栈顶。
//这是通过调用底层容器的有右值引用参数的 push_back() 函数完成的。
pop()//弹出栈顶元素。
size()//返回栈中元素的个数。
empty()//在栈中没有元素的情况下返回 true。
emplace()//用传入的参数调用构造函数,在栈顶生成对象。
//底层使用结构体实现,存储一对数据
pair p1;
p1.first = 1;
p1.second = 2.5;
cout<
map mp //typename1是键的类型,typename2是值的类型。
//map中的键是唯一的
map mp;
mp['c'] = 20;
cout << mp['c']; //答案输出20
//map可以使用it->first来访问键,it->second来访问值
//map会以键从小到大的顺序自动排序
find()
map::iterator it = mp.find('b');
cout << it->first << " " << it->second;
erase()
mp.erase(it)//it为需要删除的元素的迭代器
mp.erase(key)//key为要删除的映射的键
mp.erase(first, last)//其中,first为需要删除的区间的起始迭代器,last为需要删除的区间末尾迭代器的下一个地址,即为删除左闭右开的区间[first, last)
size()//获得map中映射的对数
clear()//清空map中的所有元素
unordered_map
unordered_map umap; //定义
堆
完全二叉树,父节点均不小于/大于子节点
make_heap(v.begin(), v.end(), greater());//生成一个小堆,第三个不加就默认最小堆,加greater()则为最大堆
pop_heap(v.begin(), v.end(), greater()); // 将堆的第零个元素与最后一个元素交换
v.pop_back(); // 删除最后位置的元素
使用双链表实现