改编自
STL 分为 容器 迭代器 空间配置器 配接器 算法 和 仿函数
命名空间
用来解决名称冲突,比如Menci和Fuxey都在自己的头文件里面写了一个work()
函数,如果我们需要同时使用这两个函数就会发生名称冲突,但是如果将这两个函数写在单独的命名空间内,就成了Menci::work()
和Fuxey::work()
,就不会发生命名冲突
STL的所有内容都包含在std的命名空间内部
如果要调用STL的sort函数就要这样写:
std::sort(a,a + n);
我们也可以把std::sort
这个函数导入到全局中,就可以直接使用该函数而不用再指明所在的命名空间
还有一种方法就是直接引入std这一个命名空间来使用其内部的所有内容,是一种比较常用的做法using namespace std;
但是这样做的后果就是更容易产生名称冲突,如果声明一个叫做max的变量就会覆盖STL中的max函数。
STL中常见的算法
1.排序
排序又分为sort排序,stable_sort
,sort
是不稳定的排序,时间复杂度是O(nlongn),stable_sort是稳定的排序,时间复杂度相同
sort
函数采用的类似于快速排序的算法,在大多数情况下可以获得最高的效率,而stable_sort
采用的是多路归并算法,可以在稳定的前提下获得较高的效率,实际上常用的是sort函数
用法sort
和stable_sort
函数的用法相同:
对于一个左闭右开的区间 [l,r)
进行排序,使用sort(l,r)
,其中l和r指向元素的迭代器,在接触迭代器之前可以理解为指向该元素的指针。一定要注意的就是区间一定要是左开右闭区间,STL中经常会出现这种区间,对一个数组的前n个元素进行排序,则对应的区间就是[a,a+n)
,因为a指向数组的第一个元素,a+n指向数组的第n个元素之后。
sort函数默认的排序是升序排序,如果需要降序,可以通过自定义比较函数来实现
bool compare(int a,int b){
return a>b;
}
std::sort(a,a+n,&compare);
给出一个完整的排序程序:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100000;
int n;
int a[MAXN];
int compare(int a,int b)
{
return a>b;
}
int main(void)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+n,&compare);
for(int i=0;i<n;i++)
{
printf("a[%d]=%d\n",i,a[i]);
}
return 0;
}
当然也可以通过写自定义函数来实现对结构体里面某一个成员进行排序见博客:结构体排序
降序排列时大于号,升序排列是小于号(不可以加等号),如果不指定就是默认为升序排列
2. 去重
使用unique函数来去除数组中重复的元素,其调用格式和sort类似,注意调用unique函数时,数组必须提前有序化
std::sort(a,a+n);
std::unique(a,a+n);
unique函数返回去重后数组额最后一个元素之后的地址,一般通过使用返回值减去首地址的方法获得不重复的元素的数量:
int cnt = std::unique(a,a+n) - a;
下面给出一个完整的去重程序代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100000;
int n;
int a[MAXN];
int main(void)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+n);
int count = unique(a,a+n) - a;
for(int i=0;i<count;i++)
{
printf("a[%d]=%d\n",i,a[i]);
}
return 0;
}
3.较大较小值
使用max和min函数来取得两个数中的较大的和较小的
int a = -1,b = 890;
x = std::max(a,b);//结果为890
y = std::min(a,b);//结果为-1
4.查找
STL中常用的查找函数有三个:lower_bound,upper_bound,binary_search,一般lower_bound最为常用
1.lower_bound用于在一个升序序列中查找莫格元素,并返回第一个不小于该元素的迭代器,如果找不到则返回指向序列中最后一个元素之后的迭代器。
2.upper_bound用于在一个升序序列中寻找莫格元素,并返回第一个大于该元素的迭代器,如果找不到就返回指向序列中最后一个元素之后的迭代器。
3.binary_search 用于确定某个元素有没有在一个升序序列中出现过,返回true或者时false。
三个查找函数的时间复杂度均为longn。
给出完整的搜索代码:
#include<bits/stdc++.h>
using namespace std;
int a[8]={-9,5,-1,2,7,1,-2,2},n=8;//-9,-2,-1,1,2,2,5,7
int main(void)
{
sort(a,a+n);
int *p1 = lower_bound(a,a+n,1);
printf("%d\n",*p1);//a[3]=1
int *p2 = lower_bound(a,a+n,2);
printf("%d\n",*p2);//a[4]=2
int *p3 = upper_bound(a,a+n,2);
printf("%d\n",*p3);//a[6]=5
int *p4 = lower_bound(a,a+n,8);
//此时如果访问p4中的元素就会发生访问越界
bool flag = binary_search(a,a+n,3);//no this number
printf("%d\n",flag);
}
容器
1.数组vector(可见于另一博客)
STL在提供了一个可变长度的数组vector
,它支持动态的插入和删除操作。
以下代码声明了一个vector
,它的每个元素类型为int,初始元素均为0。
std::vector<int >v;
以下代码声明了一个vector
,它的每个元素类型为int,初始元素数量为n
std::vector<int>v(n);
vector
提供begin()
和end()
,分别获取指向第一个元素和最后一个元素之后的迭代器。
以下代码对v中的所有元素以升序进行排列;
std::sort(v.begin(),v.end());
使用size()
得到vector
的元素数量,使用resize()
重新制定vector
的元素数量。
分别使用push_back()和pop_back()在vector的尾部加入或删除元素,这两个过程的时间复杂度都为1,使用insert()在某个特定的位置插入一个元素,时间复杂的为n。
使用erase()删除某个位置的元素,时间复杂度为n。
std::vector<int> v;
// v.size() = 0
v.push_back(23333);
// v.size() = 1, v = { 23333 }
v.insert(v.begin() + 0, 890);
// v.size() = 2, v = { 890, 23333 }
v.insert(v.begin() + 1, 12345);
// v.size() = 3, v = { 890, 12345, 23333 }
v.erase(v.begin() + 0);
// v.size() = 2, v = { 12345, 23333 }
for (nt i = 0; i < v.size(); i++) {
printf("%d\n", v[i]);
}
// 依次输出 12345、23333
v.pop_back();
// v.size() = 1, v = { 12345 }
注意,在加入元素时,如果 vector 拥有的内存空间不足以存放欲加入的元素,则 vector 会申请一块新的内存,并将旧数据拷贝过去,这个过程通常花费 n的时间。
集合set
STL 在头文件 中提供了一个有序集合 set,其中的元素全部是唯一的,并且插入进的元素自动按照升序排列,但 set 不支持通过下标定位某个元素,只能通过迭代器遍历。
以下代码声明了一个 int 类型的集合。
std::set<int> s;
使用 insert() 在集合中加入一个元素,其时间复杂度为。
使用 erase() 删除集合中某个元素或某个位置的元素,其时间复杂度均为。
set 自身提供 lower_bound() 用于定位元素,其作用与前文中的同名函数类似,也可以使用 find() 来精确查找元素。
遍历 set 只能使用迭代器。set 的迭代器为 set::iterator,其中 T 为元素类型。
std::set<int> s;
s.insert(23333);
s.insert(23333); // 重复插入无效
s.insert(66666);
s.insert(890);
s.insert(-1);
// s.size() = 4, s = { -1, 890, 23333, 66666 }
s.erase(66666);
// s.size() = 3, s = { -1, 890, 23333 }
std::set<int>::iterator p1 = s.lower_bound(555);
// *p1 = 890
std::set<int>::iterator p2 = s.find(555);
// p2 = s.end(),因为未找到 555
s.erase(p1);
// s.size() = 2, s = { -1, 23333 }
for (std::set<int>::iterator p = s.begin(); p != s.end(); p++) {
printf("%d\n", *p);
}
// 依次输出 -1、23333
字符串 string
STL 在头文件 中将一些与字符串有关的操作封装在了 string 内。
使用 cin 和 cout 来输入、输出字符串。
使用 find() 查找另一个字符串在该字符串中的出现位置,返回结果从 0 开始。
使用 c_str() 获得 string 对应的 const char * 类型数据,可用于向 C 库函数传递。
std::string s = "Menci";
int pos = s.find("23333");
// pos = string::npos,因为没有找到 23333
pos = s.find("ci");
// pos = 3,因为出现位置为第 4 个字符
char ch = s[0];
// ch = 'M'
std::cout << s << std::endl;
puts(s.c_str());
// 输出两次 Menci
队列 queue
STL 在头文件 中提供了先入先出(FIFO)队列 queue。
使用 push() 向队列中加入元素。
使用 front() 获取队首元素(并不删除)。
使用 pop() 删除队首元素。
使用 empty() 判断队列是否为空。
std::queue<int> q;
bool flag = q.empty();
// flag = true,队列初始为空
q.push(23333);
q.push(66666);
while (!q.empty()) {
printf("%d\n", q.front());
q.pop();
}
// 依次输出 23333,66666
栈 stack
STL 在头文件 提供了后入先出(LIFO)栈 stack。
使用 push() 向栈中加入元素。
使用 top() 获取栈顶元素(并不删除)。
使用 pop() 删除栈顶元素。
使用 empty() 判断栈是否为空。
std::stack<int> s;
bool flag = s.empty();
// flag = true,栈初始为空
s.push(23333);
s.push(66666);
while (!s.empty()) {
printf("%d\n", s.top());
s.pop();
}
// 依次输出 66666,23333
优先队列 priority_queue
STL 在头文件 中提供优先队列 priority_queue,在任意时间都能取出队列中的最大值。
使用 push() 向优先队列中加入元素,其时间复杂度为。
使用 top() 获取优先队列中最大的元素(并不删除),其时间复杂度为。
使用 pop() 删除优先队列中最大元素,其时间复杂度为。
使用 empty() 判断优先队列是否为空。
std::priority_queue<int> q;
bool flag = q.empty();
// flag = true,优先队列初始为空
q.push(23333);
q.push(-1);
q.push(66666);
while (!q.empty()) {
printf("%d\n", q.top());
q.pop();
}
// 依次输出 66666,23333,-1
priority_queue 默认提供队列中的最大值,也可以以以下声明方式让 priority_queue 提供最小值。
std::priority_queue<T, std::vector<T>, std::greater<T> > q;
注意把三个 T 换成优先队列中元素的类型(如 int);std::greater 的右边要加一个空格,否则会被编译器误认为是 >> 右移运算符。