STL
STL 是“Standard Template Library”的缩写,中文译为“标准模板库”。
STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离
例如,vector 的底层为顺序表(数组),list 的底层为双向链表,deque 的底层为循环队列,set 的底层为红黑树,hash_set 的底层为哈希表。
Iterator
- 迭代器是一个“可以遍历STL容器内全部或部分元素”的对象
- 迭代器指出容器中的一个特定位置
- 迭代器就如同一个指针
- 迭代器提供对一个容器中的对象的访问方法,并且可以定义容器中对象的范围
迭代器的功能分类
1)正向迭代器
运算符 | 功能 |
---|---|
++ | 让迭代器指向下一元素 |
==,!= | 判断两个迭代器是否指向同一位置并返回结果 |
= | 将右侧的值代入左侧迭代器所引用的元素的位置 |
* | 返回该位置的元素 |
2)双向迭代器
双向迭代器具有正向迭代器的全部功能。除此之外,若 p 是一个双向迭代器,则--p
和p--
都是有定义的。--p
使得 p 朝和++p
相反的方向移动。
3)随机访问迭代器
随机访问迭代器具有双向迭代器的全部功能。若 p 是一个随机访问迭代器,i 是一个整型变量或常量,则 p 还支持以下操作:
p+=i
:使得 p 往后移动 i 个元素。p-=i
:使得 p 往前移动 i 个元素。p+i
:返回 p 后面第 i 个元素的迭代器。p-i
:返回 p 前面第 i 个元素的迭代器。p[i]
:返回 p 后面第 i 个元素的引用。
此外,两个随机访问迭代器 p1、p2 还可以用 <、>、<=、>= 运算符进行比较。p1<p2
的含义是:p1 经过若干次(至少一次)++
操作后,就会等于 p2。其他比较方式的含义与此类似。
对于两个随机访问迭代器 p1、p2,表达式p2-p1
也是有定义的,其返回值是 p2 所指向元素和 p1 所指向元素的序号之差。
容器 | 迭代器功能 |
---|---|
vector | 随机访问 |
deque | 随机访问 |
list | 双向 |
set / multiset | 双向 |
map / multimap | 双向 |
unordered_set / unordered_map | 正向迭代器 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
priority_queue | 不支持迭代器 |
常用函数
isalnum()
用来判断一个字符是否为数字或者字母,也就是说判断一个字符是否属于a~ z||A~ Z||0~9。isalpha()
用来判断一个字符是否为字母isdigit()
用来检测一个字符是否是十进制数字0-9islower()
用来判断一个字符是否为小写字母,也就是是否属于a~z。isupper()
和islower()
相反,用来判断一个字符是否为大写字母。
Vector
vector 容器是 STL中最常用的容器之一,它和 array 容器非常类似,都可以看做是对 C++普通数组的“升级版”。不同之处在于,array 实现的是静态数组(容量固定的数组),而 vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会动态调整所占用的内存空间,整个过程无需人工干预。
vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)。
#include <bits/stdc++.h>
using namespace std;
vector <int> a;//存储int的vector
vector <vector <int> > b;//vector里面存vector
vector<vector<int> > c(n, vector<int>(n));//n行n列
vector <int> d[100];//100个vector
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回向量的元素数 | O(1) |
resize(n) | 调整容器的大小,使其包含n个元素,新增元素调用默认构造函数 | |
resize(n,x) | 调整容器的大小,使其包含n个元素,新增元素初始化为x | |
reserve(n) | 更改容器的容量(capacity)为n | |
push_back(x) | 在向量末尾添加元素x | O(1) |
pop_back(x) | 删除向量最后一个元素 | O(1) |
emplace_back(x) | 在向量末尾添加元素x,比push_back()效率高 | O(1) |
begin() | 返回指向向量开头的迭代器 | O(1) |
end() | 返回指向向量末尾(最后一个元素的后一个位置)的迭代器 | O(1) |
insert(p,x) | 在向量位置p处插入元素x,p为指向该位置的迭代器 | O(n) |
erase§ | 删除向量位置p处的元素,p为指向该位置的迭代器 | O(n) |
erase(first,last) | 删除向量位置 [first ,last) 区间内的所有元素,first 和 last 为迭代器 | O(n) |
clear() | 删除向量中的所有元素 | O(n) |
assign(const_iterator first,const_iterator last) | 将一个容器中的 **[first,last)**范围内的元素拷贝到另一个容器中 | O(n) |
reverse(const_iterator first,const_iterator last) | 反转在**[first,last)**范围内的顺序,reverse函数无返回值 | O(n) |
emplace_back()
和 push_back()
的区别,就在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back()
在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
emplace_back()
(C++ 11新增):在容器尾部添加一个元素,调用构造函数原地构造,不需要触发拷贝构造和移动构造。因此比push_back()
更加高效。
//#include <bits/stdc++.h>
#include <vector>
#include <iostream>
using namespace std;
vector <int> a;
int main()
{
a={1,2,3,4,5,6};
//利用循环直接遍历
for(int i=0;i<a.size();i++){
cout<<a[i]<<" ";
}
cout<<endl;
a.pop_back();
a.emplace_back(7);
a.erase(a.begin());
a.insert(a.begin(),0);
//正向迭代器遍历
for(vector<int>::iterator it = a.begin(); it != a.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
//逆向迭代器遍历
for(vector<int>::reverse_iterator it = a.rbegin(); it != a.rend(); it++){
cout<<*it<<" ";
}
a.clear();
return 0;
}
输出结果:
1 2 3 4 5 6
0 2 3 4 5 7
7 5 4 3 2 0
list
底层是以双向链表的形式实现的,插入和删除效率极高,但访问效率低,不能直接访问,需要使用迭代器访问
实际场景中,如何需要对序列进行大量添加或删除元素的操作,而直接访问元素的需求却很少,这种情况建议使用 list 容器存储序列。
list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中。
// #include <bits/stdc++.h>
#include <list>
#include <iostream>
using namespace std;
list <int> a;
list <list <int> > b;
list <int> c[100];
list <int> d(10);//其中包含 10 个元素,每个元素的值都为相应类型的默认值(int类型的默认值为 0)
list<int> e(10, 5);//包含 10 个元素并且值都为 5
list<int> f(e);//在已有 list 容器的情况下,通过拷贝该容器可以创建新的 list 容器
//通过拷贝其他类型容器(或者普通数组)中指定区域内的元素,可以创建新的 list 容器
int g[] = { 1,2,3,4,5 };
list<int> h(g, g+3);//list容器里有1,2,3
list<int>i(h.begin()+1, h.end());//拷贝h容器中的2,3
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回表的元素数 | O(1) |
begin() | 返回指向表开头的迭代器 | O(1) |
end() | 返回指向表末尾(最后一个元素的后一个位置)的迭代器 | O(1) |
push_front(x) | 在表开头添加元素x | O(1) |
push_back(x) | 在表末尾添加元素x | O(1) |
emplace_front(x) | 在表开头添加元素x | O(1) |
emplace_back(x) | 在表末尾添加元素x | O(1) |
pop_front(x) | 删除位于表头的元素 | O(1) |
pop_back() | 删除位于表尾的元素 | O(1) |
insert(p,x) | 在表p位置处插入元素x,p为指向该位置的迭代器 | O(1) |
emplace(p,x) | 在表p位置处插入元素x,p为指向该位置的迭代器,效率比 insert() 方法高 | O(1) |
erase§ | 删除表中p位置的元素,p为指向该位置的迭代器 | O(1) |
clear() | 删除表中所有元素 | O(n) |
empty() | 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false | O(1) |
sort() | 通过更改容器中元素的位置,将它们进行排序 | O(n*logn) |
reverse() | 反转容器中元素的顺序 | O(n) |
// #include <bits/stdc++.h>
#include <list>
#include <iostream>
using namespace std;
list <int> a={6,7,8,9};
bool cmp(int x,int y){
return x>y;
}
int main()
{
for(int i=0;i<5;i++) a.push_back(i+1);
//反向迭代器遍历
for(list <int>::reverse_iterator it = a.rbegin();it != a.rend();it++)
cout<<*it<<" ";
cout<<endl;
a.pop_front();
a.pop_back();
a.sort();
//迭代器遍历
for(list <int>::iterator it = a.begin();it != a.end();it++)
cout<<*it<<" ";
cout<<endl;
a.sort(cmp);
//迭代器遍历
for(list <int>::iterator it = a.begin();it != a.end();it++)
cout<<*it<<" ";
cout<<endl;
}
输出结果:
5 4 3 2 1 9 8 7 6
1 2 3 4 7 8 9
9 8 7 4 3 2 1
stack
stack不支持Iterator
先进后出
// #include <bits/stdc++.h>
#include <stack>
#include <list>
#include <iostream>
using namespace std;
stack <int> a;
stack <int,list<int> > b;//stack 容器适配器的模板有两个参数。第一个参数是存储对象的类型,第二个参数是底层容器的类型
stack <int> c(a);
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回栈的元素个数 | O(1) |
top() | 返回栈顶元素 | O(1) |
pop() | 弹出(删除)栈顶元素 | O(1) |
push(x) | 向栈中添加元素x | O(1) |
emplace(x) | 向栈中添加元素x | O(1) |
empty() | 判断栈是否为空,为空时返回true | O(1) |
pop()不返回元素
// #include <bits/stdc++.h>
#include <stack>
#include <list>
#include <iostream>
using namespace std;
list <int> a;
int main()
{
a={1,2,3,4,5};
stack <int,list <int> > b(a);//用另一个容器来初始化,只要堆栈的底层容器类型和这个容器的类型相同
//复制一个temp栈用来遍历
stack <int,list <int> > temp(b);
while(!temp.empty()){
cout<<temp.top()<<" ";
temp.pop();
}
cout<<endl;
//破坏性遍历
while(!b.empty()){
cout<<b.top()<<" ";
b.pop();
}
}
输出结果:
5 4 3 2 1
5 4 3 2 1
queue
queue不支持Iterator
先进先出
// #include <bits/stdc++.h>
#include <queue>
#include <iostream>
using namespace std;
queue <int> a;
queue <int> b(a);
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回队列中的元素个数 | O(1) |
front() | 返回队头元素 | O(1) |
back() | 返回队尾元素 | O(1) |
pop() | 队头元素出队 | O(1) |
push(x) | 向队尾添加元素x | O(1) |
emplace(x) | 向队尾添加元素x | O(1) |
empty() | 判断队列是否为空,为空返回true | O(1) |
swap(queue a,queue b) | 交换两个队列的元素 | O(1) |
// #include <bits/stdc++.h>
#include <queue>
#include <iostream>
using namespace std;
queue <int> a;
int main()
{
for(int i=0;i<5;i++)
a.emplace(i);
queue <int> b(a);
while(!b.empty()){
cout<<b.front()<<" ";
b.pop();
}
cout<<endl;
cout<<a.front()<<" "<<a.back()<<endl;
for(int i=5;i>0;i--)
b.emplace(i);
swap(a,b);
while(!b.empty()){
cout<<b.front()<<" ";
b.pop();
}
}
0 1 2 3 4
0 4
0 1 2 3 4
deque
deque 是 double-ended queue 的缩写,又称双端队列容器。
deque 容器和 vecotr 容器有很多相似之处,比如:
-
deque 容器也擅长在序列尾部添加或删除元素( 时间复杂度为O(1) ),而不擅长在序列中间添加或删除元素。
-
deque 容器也可以根据需要修改自身的容量和大小。
和 vector 不同的是,deque 还擅长在序列头部添加或删除元素,所耗费的时间复杂度也为常数阶O(1)。并且更重要的一点是,deque 容器中存储元素并不能保证所有元素都存储到连续的内存空间中。
当需要向序列两端频繁的添加或删除元素时,应首选 deque 容器。
// #include<bits/stdc++.h>
#include <iostream>
#include <deque>
using namespace std;
int main()
{
deque <int> a;//创建一个没有任何元素的空 deque 容器
deque <int> b(10);//创建一个具有 10 个元素(默认都为 0)的 deque 容器
deque <int> c(10 ,5);//创建了一个包含 10 个元素(值都为 5)的 deque 容器
deque <int> d(c);//在已有 deque 容器的情况下,可以通过拷贝该容器创建一个新的 deque 容器
//采用此方式,必须保证新旧容器存储的元素类型一致
int num[]={1,2,3,4,5};//通过拷贝其他类型容器中指定区域内的元素(也可以是普通数组),可以创建一个新容器
deque <int> e(num,num+5);
}
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回元素个数 | O(1) |
begin() | 返回指向第一个元素的迭代器 | O(1) |
rbegin() | 返回指向最后一个元素的迭代器 | O(1) |
end() | 返回指向最后一个元素后一个位置的迭代器 | O(1) |
rend() | 返回指向第一个元素前一个位置的迭代器 | O(1) |
push_front(x) | 在容器开头添加元素x | O(1) |
emplace_front(x) | 在容器开头添加元素x | O(1) |
push_back(x) | 在容器的末尾添加元素x | O(1) |
emplace_back(x) | 在容器的末尾添加元素x | O(1) |
pop_front() | 删除容器开头元素 | O(1) |
pop_back() | 删除容器末尾元素 | O(1) |
emplace(p,x) | 在表p位置处插入元素x,p为指向该位置的迭代器,效率比 insert() 方法高 | O(1) |
insert(p,x) | 在表p位置处插入元素x,p为指向该位置的迭代器 | O(n) |
erase§ | 删除容器中p位置的元素,p为指向该位置的迭代器 | O(n) |
clear() | 删除容器中所有元素 | O(n) |
empty() | 判断容器是否为空,为空返回true | O(1) |
priority_queue
底层存储结构采用堆数据结构
底层容器是vector或者deque,vector 或 deque 容器并没有提供实现 priority_queue 容器适配器 “First in,Largest out” 特性的功能,因此 STL 选择使用堆来重新组织 vector 或 deque 容器中存储的数据,从而实现该特性。
priority_queue不支持迭代器
priority_queue 容器适配器定义了一个元素有序排列的队列, **默认队列头部的元素优先级最高。**因为它是一个队列,所以只能访问第一个元素,这也意味着优先级最高的元素总是第一个被处理
// #include <bits/stdc++.h>
#include <queue>
#include <iostream>
using namespace std;
priority_queue <int> a;
//可以用适当类型的对象初始化一个优先级队列:
int num[]={2,4,1,5,3};
priority_queue <int> b {begin(num),end(num)};//初始化列表中的序列可以来自于任何容器,并且不需要有序。优先级队列会对它们进行排序。
priority_queue <int> c(b);//拷贝构造函数会生成一个和现有对象同类型的 priority_queue 对象,它是现有对象的一个副本。
priority_queue是一种能根据元素优先级进行插入,查询,删除操作的队列。操作与queue 相同。
优先队列默认使用operator < 来比较大小,并且大的优先。
priority_queue 模板有 3 个参数,其中两个有默认的参数;第一个参数是存储对象的类型,第二个参数是存储元素的底层容器,第三个参数是函数对象,它定义了一个用来决定元素顺序的断言
// #include <bits/stdc++.h>
#include <queue>
#include <iostream>
using namespace std;
//降序队列(大顶堆):使用 < 比较大小()
priority_queue <int, vector <int>, less<int> > a;
//升序队列(小顶堆):使用 > 比较大小
priority_queue <int, vector <int>, greater<int> > b;
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回队列元素个数 | O(1) |
top() | 返回队头元素 | O(1) |
push(x) | 向队列中添加元素x | O(logn) |
pop() | 队头元素出队 | O(logn) |
empty() | 判断队列是否为空,为空时返回true | O(1) |
对于结构体,我们需要重载运算符,因为排序要使用 > (或 < )
//#include <bits/stdc++.h>
#include <queue>
#include <iostream>
using namespace std;
struct Point
{
int x,y;
friend bool operator<(Point a, Point b){
return a.x<b.x;
}
friend bool operator>(Point a, Point b){
return a.x>b.x;
}
};
priority_queue<Point> a;//默认是大顶堆
priority_queue<Point,vector<Point>,greater<Point> >b;
int main()
{
a.push({1,2});
a.push({4,5});
a.push({3,5});
a.push({2,5});
while(!a.empty()){
cout <<a.top().x<<" "<<a.top().y<<endl;
a.pop();
}
cout<<endl;
b.push({1,2});
b.push({4,5});
b.push({3,5});
b.push({2,5});
while(!b.empty()){
cout <<b.top().x<<" "<<b.top().y<<endl;
b.pop();
}
}
输出结果:
4 5
3 5
2 5
1 2
1 2
2 5
3 5
4 5
priority_queue 底层使用堆存储结构,大顶堆(小顶堆)中的数据整体依然无序,但是最大(最小)的元素肯定是开头的第一个元素。
第一个元素出队后,剩下的元素重新组织成大顶堆(小顶堆)。
set
set中存储的也是键值对,其键和值完全相同
根据元素的值进行排序的有序集合,集合中不存在重复元素
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回当前 set 容器中存有元素的个数。 | O(1) |
empty() | 判断当前容器是否为空,为空返回true | O(1) |
clear() | 清空 set 容器中所有的元素,即令 set 容器的 size() 为 0。 | O(n) |
begin() | 返回指向容器中第一个(注意,是已排好序的第一个)元素的迭代器。 | O(1) |
end() | 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的迭代器。 | O(1) |
insert(val) | 向当前 set 容器中添加新元素。 | O(logn) |
emplace(val) | 向当前 set 容器中添加新元素。其效果和 insert() 一样,但效率更高。 | O(logn) |
erase(val) | 删除 set 容器中的元素val。 | O(logn) |
find(val) | 在 set 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的迭代器;反之,则返回和 end() 方法一样的迭代器。 | O(logn) |
lower_bound(val) | 返回一个指向当前 set 容器中第一个大于或等于 val 的元素的迭代器。 | O(logn) |
upper_bound(val) | 返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。 | O(logn) |
equal_range(val) | 该方法返回一个 pair 对象(包含 2 个迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)。 | O(logn) |
count(val) | 在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1。 | O(logn) |
set 容器支持随时向内部添加新的元素,因此创建空 set 容器的方法是经常使用的
//#include <bits/stdc++.h>
#include <set>
#include <iostream>
using namespace std;
set <int> a;//默认升序
set <int> b={2,4,3,5,1};//创建的同时初进行始化
set <int> c(b);//拷贝创建
set <int> d(++c.begin(),--c.end());//部分拷贝,[start,end)左闭右开
set <int,greater<int> > e={2,4,3,5,1};//降序
int main()
{
for(set <int>::iterator it = b.begin();it != b.end();it++)
cout<<*it<<" ";
cout<<endl;
for(set <int>::iterator it = c.begin();it != c.end();it++)
cout<<*it<<" ";
cout<<endl;
for(set <int>::iterator it = d.begin();it != d.end();it++)
cout<<*it<<" ";
cout<<endl;
for(set <int>::iterator it = e.begin();it != e.end();it++)
cout<<*it<<" ";
cout<<endl;
}
输出结果:
1 2 3 4 5
1 2 3 4 5
2 3 4
5 4 3 2 1
bitset
位图的引入
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?
要判断一个数是否在某一堆数中,我们可能会想到如下方法:
- 将这一堆数进行排序,然后通过二分查找的方法判断该数是否在这一堆数中。
- 将这一堆数插入到unordered_set容器中,然后调用find函数判断该数是否在这一堆数中。
单从方法上来看,这两种方法都是可以,而且效率也不错,第一种方法的时间复杂度是O(NlogN),第二种方法的时间复杂度是O(N)。
但问题是这里有40亿个数,若是我们要将这些数全部加载到内存当中,那么将会占用16G的空间,空间消耗是很大的。因此从空间消耗来看,上面这两种方法实际都是不可行的。
位图解决
实际在这个问题当中,我们只需要判断一个数在或是不在,即只有两种状态,那么我们可以用一个比特位来表示数据是否存在,如果比特位为1则表示存在,比特位为0则表示不存在。比如:
无符号整数总共有232个,因此记录这些数字就需要232个比特位,也就是512M的内存空间,内存消耗大大减少。
位图的概念
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。
bitset的定义方式
方式一: 构造一个16位的位图,所有位都初始化为0。
bitset<16> bs1; //0000 0000 0000 0000
方式二: 构造一个16位的位图,根据所给值初始化位图的前n位。
bitset<16> bs2(0xfa5); //0000 1111 1010 0101
方式三: 构造一个16位的位图,根据字符串中的0/1序列初始化位图的前n位。
bitset<16> bs3(string("10111001")); //0000 0000 1011 1001
bitset成员函数的使用
bitset中常用的成员函数如下:
成员函数 | 功能 |
---|---|
set() | 设置指定位 或 所有位 |
reset() | 清空指定位 或 所有位 |
flip() | 反转指定位 或 所有位 |
test() | 获取指定位的状态 |
count() | 获取被设置位的个数 |
size() | 获取可以容纳的位的个数 |
any() | 如果有任何一个位被设置则返回true |
none() | 如果没有位被设置则返回true |
all() | 如果所有位都被设置则返回true |
注意: 使用成员函数set、reset、flip时,若指定了某一位则操作该位,若未指定位则操作所有位。
#include <bits/stdc++.h>
using namespace std;
int main()
{
bitset<8> bs;
bs.set(2); //设置第2位
bs.set(4); //设置第4位
cout << bs << endl; //00010100
bs.flip(); //反转所有位
cout << bs << endl; //11101011
cout << bs.count() << endl; //6
cout << bs.test(3) << endl; //1
bs.reset(0); //清空第0位
cout << bs << endl; //11101010
bs.flip(7); //反转第7位
cout << bs << endl; //01101010
cout << bs.size() << endl; //8
cout << bs.any() << endl; //1
bs.reset(); //清空所有位
cout << bs.none() << endl; //1
bs.set(); //设置所有位
cout << bs.all() << endl; //1
return 0;
}
输出结果:
00010100
11101011
6
1
11101010
01101010
8
1
1
1
bitset运算符的使用
一、bitset中 >> ,<< 运算符的使用。
bitset容器对>>、<<运算符进行了重载,我们可以直接 使用>>、<<运算符对biset容器定义出来的对象进行输入输出操作。
#include <bits/stdc++.h>
using namespace std;
int main()
{
bitset<8> bs;
cin >> bs; //10110
cout << bs << endl; //00010110
return 0;
}
输出结果:
00010110
二、bitset中赋值运算符、关系运算符、复合赋值运算符、单目运算符的使用。
bitset容器中不仅对赋值运算符和一些关系运算符进行了重载,而且对一些复合赋值运算符和单目运算符也进行了重载,我们可以直接使用这些运算符对各个位图进行操作。
<< //二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
>> //二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
包括如下运算符:
- 赋值运算符:=
- 关系运算符:==、!=
- 复合赋值运算符:&=、|=、^=、<<=、>>=
- 单目运算符:~
#include <bits/stdc++.h>
using namespace std;
int main()
{
bitset<8> bs1(string("10101010"));
bitset<8> bs2(string("10101010"));
bs1 >>= 1;
cout << bs1 << endl; //01010101
bs2 |= bs1;
cout << bs2 << endl; //11111111
bs1 = ~bs1;
cout << bs1 << endl; //10101010
cout<<(bs1 == bs2)<<endl; //false
return 0;
}
输出结果:
01010101
11111111
10101010
0
三、bitset中位运算符的使用。
bitset容器中同时也对三个位运算符进行了重载,我们可以直接使用&、|、^(按位与,按位或,按位异或)对各个位图进行操作。
#include <bits/stdc++.h>
using namespace std;
int main()
{
bitset<8> bs1(string("10101010"));
bitset<8> bs2(string("01010101"));
cout << (bs1 & bs2) << endl; //00000000
cout << (bs1 | bs2) << endl; //11111111
cout << (bs1 ^ bs2) << endl; //11111111
return 0;
}
输出结果:
00000000
11111111
11111111
四、bitset中[ ]运算符的使用。
bitset容器中对[ ]运算符进行了重载,我们可以直接使用[ ]对指定位进行访问或修改。
#include <bits/stdc++.h>
using namespace std;
int main()
{
bitset<8> bs(string("00110101"));
cout << bs[0] << endl; //1
bs[0] = 0;
cout << bs << endl; //00110100
return 0;
}
输出结果:
1
00110100
map
map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对。其中,各个键值对的键和值可以是任意数据类型,包括 C++基本数据类型(int、double 等)、使用结构体或类自定义的类型。(使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改)
前面提到,map 容器存储的都是 pair 类型的键值对元素,更确切的说,该容器存储的都是 pair<const K, T>
类型(其中 K 和 T 分别表示键和值的数据类型)的键值对元素。
//#include <bits/stdc++.h>
#include <map>
#include <iostream>
using namespace std;
//创建出一个空的 map 容器
map<string,int> a;
//创建的同时初始化
map<string,int> b={{"c",3},{"d",4},{"a",1},{"b",2}};//默认情况下,调用 std::less<T> 规则,根据容器内各键值对的键的大小,做升序排序。
//拷贝构造
map<string,int> c(b);
//部分拷贝,[start,end)左闭右开
map<string,int> d(++c.begin(),--c.end());
map<string,int,greater<string> > e={{"c",3},{"d",4},{"a",1},{"b",2}};//调用 std::greater<T>,做降序排序。
int main()
{
for(map<string,int>::iterator it = b.begin();it != b.end();it++)
cout <<it->first<<" "<<it->second<<endl;
cout<<endl;
for(map<string,int>::iterator it = c.begin();it != c.end();it++)
cout <<it->first<<" "<<it->second<<endl;
cout<<endl;
for(map<string,int>::iterator it = d.begin();it != d.end();it++)
cout <<it->first<<" "<<it->second<<endl;
cout<<endl;
for(map<string,int>::iterator it = e.begin();it != e.end();it++)
cout <<it->first<<" "<<it->second<<endl;
}
输出结果:
a 1
b 2
c 3
d 4
a 1
b 2
c 3
d 4
b 2
c 3
d 4
c 3
b 2
a 1
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回当前 map 容器中存有键值对的个数。 | O(1) |
clear() | 清空 map 容器中所有的键值对。 | O(n) |
begin() | 返回指向容器中第一个(注意,是已排好序的第一个)键值对的迭代器。 | O(1) |
end() | 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的迭代器。 | O(1) |
insert(make_pair(key,val)) | 向 map 容器中插入键值对。 | O(logn) |
emplace(make_pair(key,val)) | 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。 | O(logn) |
erase() | 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。 | O(logn) |
find(key) | 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的迭代器;反之,则返回和 end() 方法一样的迭代器。 | O(logn) |
count(key) | 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。 | O(logn) |
empty() | 若容器为空,则返回 true;否则 false。 | O(1) |
注:
operator[]
map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值
可以通过 a[key]=val 来修改元素(赋值),若key不存在,相当于新增了一个元素。
unordered_map和unordered_set的底层实现都是hash table,内部存储并不保证顺序,也不排序,也就是说你插入多个元素时的插入顺序和你从begin到end遍历时的顺序未必是一致的,这个顺序性它是不保证的。再加上,在需要的时候比如装载因子达到某一个值的时候,所有元素会重新hash,重新hash后,各个元素的位置都会重新计算,都可能都会发生移动,就更加不保证顺序了。
unordered_set
unordered_set 容器,可直译为“无序 set 容器”,即 unordered_set 容器和 set 容器很像,唯一的区别就在于 set 容器会自行对存储的数据进行排序,而 unordered_set 容器不会。
顺序和插入顺序无关
底层实现为哈希表,建立比较消耗时间,而查询,插入,删除的速度都在常数级别,可以看出O(1),但如果出现hash冲突,性能会下降很多,最坏情况下会退化成O(n)
总的来说,unordered_set 容器具有以下几个特性:
- 不再以键值对的形式存储数据,而是直接存储数据的值。
- 容器内部存储的各个元素的值都互不相等,且不能被修改。
- 不会对内部存储的数据进行排序。(这和该容器底层采用哈希表结构存储数据有关)
unordered_set 容器的类模板定义如下:
template < class Key, //容器中存储元素的类型
class Hash = hash<Key>, //确定元素存储位置所用的哈希函数
class Pred = equal_to<Key>, //判断各个元素是否相等所用的函数
class Alloc = allocator<Key> //指定分配器对象的类型
> class unordered_set;
参数 | 含义 |
---|---|
Key | 确定容器存储元素的类型,如果将 unordered_set 看做是存储键和值相同的键值对的容器,则此参数则用于确定各个键值对的键和值的类型,因为它们是完全相同的,因此一定是同一数据类型的数据。 |
Hash = hash | 指定 unordered_set 容器底层存储各个元素时,所使用的哈希函数。需要注意的是,默认哈希函数 hash 只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。 |
Pred = equal_to | unordered_set 容器内部不能存储相等的元素,而衡量 2 个元素是否相等的标准,取决于该参数指定的函数。 默认情况下,使用 STL 标准库中提供的 equal_to 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。 |
如果 unordered_set 容器中存储的元素为自定义的数据类型,则默认的哈希函数 hash 以及比较函数 equal_to 将不再适用,只能自己设计适用该类型的哈希函数和比较函数,并显式传递给 Hash 参数和 Pred 参数。
//#include <bits/stdc++.h>
#include <unordered_set>
#include <iostream>
using namespace std;
//创建空的 unordered_set 容器
unordered_set<string> a;
//在创建 unordered_set 容器的同时,可以完成初始化操作
unordered_set<string> b={"hello","world","ni","hao"};
//拷贝构造
unordered_set<string> c(b);
//部分拷贝
unordered_set<string> d(++c.begin(), c.end());//unordered_set支持的迭代器为正向迭代器
int main()
{
for(unordered_set<string>::iterator it = a.begin(); it != a.end();it++)
cout<<*it<<" ";
cout<<endl;
for(unordered_set<string>::iterator it = b.begin(); it != b.end();it++)
cout<<*it<<" ";
cout<<endl;
for(unordered_set<string>::iterator it = c.begin(); it != c.end();it++)
cout<<*it<<" ";
cout<<endl;
for(unordered_set<string>::iterator it = d.begin(); it != d.end();it++)
cout<<*it<<" ";
cout<<endl;
}
输出结果:
hao ni world hello
hao ni world hello
hello world ni
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回当前容器中存有元素的个数。 | O(1) |
begin() | 返回指向容器中第一个元素的正向迭代器。 | O(1) |
end() | 返回指向容器中最后一个元素之后位置的正向迭代器。 | O(1) |
empty() | 若容器为空,则返回 true;否则 false。 | O(1) |
find(key) | 查找以值为 key 的元素,如果找到,则返回一个指向该元素的正向迭代器;反之,则返回一个指向容器中最后一个元素之后位置的迭代器(如果 end() 方法返回的迭代器)。 | O(1) |
count(key) | 在容器中查找值为 key 的元素的个数。 | O(logn) |
insert() | 向容器中添加新元素。 | O(1) |
emplace() | 向容器中添加新元素,效率比 insert() 方法高。 | O(1) |
erase(val) | 删除指定元素。 | O(1) |
clear() | 清空容器,即删除容器中存储的所有元素。 | O(n) |
unordered_map
unordered_map 容器和 map 容器一样,以键值对(pair类型)的形式存储数据,存储的各个键值对的键互不相同且不允许被修改。但由于 unordered_map 容器底层采用的是哈希表存储结构,该结构本身不具有对数据的排序功能,所以此容器内部不会自行对存储的键值对进行排序。
建立比较消耗时间,而查询,插入,删除的速度都在常数级别,可以看出O(1),但如果出现hash冲突,性能会下降很多,最坏情况下会退化成O(n)
unordered_map 容器模板的定义如下所示:
template < class Key, //键值对中键的类型
class T, //键值对中值的类型
class Hash = hash<Key>, //容器内部存储键值对所用的哈希函数
class Pred = equal_to<Key>, //判断各个键值对键相同的规则
class Alloc = allocator< pair<const Key,T> > // 指定分配器对象的类型
> class unordered_map;
参数 | 含义 |
---|---|
<key,T> | 前 2 个参数分别用于确定键值对中键和值的类型,也就是存储键值对的类型。 |
Hash = hash | 用于指明容器在存储各个键值对时要使用的哈希函数,默认使用 STL 标准库提供的 hash 哈希函数。注意,默认哈希函数只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。 |
Pred = equal_to | 要知道,unordered_map 容器中存储的各个键值对的键是不能相等的,而判断是否相等的规则,就由此参数指定。默认情况下,使用 STL 标准库中提供的 equal_to 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。 |
当无序容器中存储键值对的键为自定义类型时,默认的哈希函数 hash 以及比较函数 equal_to 将不再适用,只能自己设计适用该类型的哈希函数和比较函数,并显式传递给 Hash 参数和 Pred 参数。
//#include <bits/stdc++.h>
#include <unordered_map>
#include <iostream>
using namespace std;
//创建空的 unordered_map 容器
unordered_map<string,int> a;
//在创建 unordered_map 容器的同时,可以完成初始化操作
unordered_map<string,int> b={{"hello",1},{"world",2},{"ni",3},{"hao",4}};
//拷贝构造
unordered_map<string,int> c(b);
//部分拷贝
unordered_map<string,int> d(++c.begin(),c.end());
int main()
{
for(unordered_map<string,int>::iterator it = a.begin();it != a.end();it++)
cout<<it->first<<" "<<it->second<<endl;
cout<<endl;
for(unordered_map<string,int>::iterator it = b.begin();it != b.end();it++)
cout<<it->first<<" "<<it->second<<endl;
cout<<endl;
for(unordered_map<string,int>::iterator it = c.begin();it != c.end();it++)
cout<<it->first<<" "<<it->second<<endl;
cout<<endl;
for(unordered_map<string,int>::iterator it = d.begin();it != d.end();it++)
cout<<it->first<<" "<<it->second<<endl;
cout<<endl;
}
输出结果:
hao 4
ni 3
world 2
hello 1
hao 4
ni 3
world 2
hello 1
hello 1
world 2
ni 3
函数名 | 功能 | 复杂度 |
---|---|---|
size() | 返回当前容器中存有键值对的个数。 | O(1) |
begin() | 返回指向容器中第一个键值对的正向迭代器。 | O(1) |
end() | 返回指向容器中最后一个键值对之后位置的正向迭代器。 | O(1) |
empty() | 若容器为空,则返回 true;否则 false。 | O(1) |
find(key) | 查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。 | O(1) |
count(key) | 在容器中查找以 key 键的键值对的个数。 | O(logn) |
insert() | 向容器中添加新键值对。 | O(1) |
emplace() | 向容器中添加新键值对,效率比 insert() 方法高。 | O(1) |
erase(key) | 通过键值,删除指定键值对。 | O(1) |
clear() | 清空容器,即删除容器中存储的所有键值对。 | O(n) |
STL算法
STL算法存在 < algorithm >库中,主要有sort
,upper_bound
,lower_bound
,binary_search
sort
使用的排序方法为经过优化的快速排序,复杂度比较稳定,为 O(nlogn)
//#include <bits/stdc++.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
struct Point
{
int x, y;
};
Point a[100];
vector <Point> b;
bool cmp(Point a,Point b){
return a.x > b.x;
}
int main()
{
sort(a,a+100);//默认使用 <,升序排列
sort(b.begin(),b.end(),cmp);//定义cmp,降序排列
}
count,find
//count()
template <class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type
count (
InputIterator first,
InputIterator last,
const T& val
);
//find()
InputIterator find (InputIterator first, InputIterator last, const T& val);
//因为 first 和 last 的类型为输入迭代器,因此该函数适用于所有的序列式容器。
first:查询的起始位置,为一个迭代器
last: 查询的结束位置,为一个迭代器
通过比较是否等于 val 返回[first,last]与 val相等的数值的个数。
注意本函数与find的区别:
count 返回值为查找的个数
find 返回值为一个迭代器
find()
函数的底层实现,其实就是用==
运算符将 val
和 [first, last)
区域内的元素逐个进行比对。这也就意味着,[first, last)
区域内的元素必须支持==
运算符。
find()
会返回一个输入迭代器,当 find()
函数查找成功时,其指向的是在 [first, last)
区域内查找到的第一个目标元素;如果查找失败,则该迭代器的指向和 last
相同。
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<int> a = {1, 1, 1, 2, 2, 3, 4, 4, 5};
for(int i = 1; i <= 5; ++i) {
cout << i << "的个数为:" << count(a.begin(), a.end(), i) << endl;
}
cout << "第一个1在第" << find(a.begin(), a.end(), 1) - a.begin() << "位" << endl;
}
输出结果
1的个数为:3
2的个数为:2
3的个数为:1
4的个数为:2
5的个数为:1
第一个1在第0位
accumulate
accumulate
函数将一段数字从头到尾累加起来,或者使用指定的运算符进行运算
accumulate
函数的前两个参数指定累加的范围,第三个参数为累加的初值,第四个参数为进行的操作,默认为累加
template<class InputIterator, class Type>
Type accumulate(
InputIterator _First,
InputIterator _Last,
Type _Val
);
template<class InputIterator, class Type, class Fn2>
Type accumulate(
InputIterator _First,
InputIterator _Last,
Type _Val,
BinaryOperation _Binary_op //自定义二进制操作
);
!!! accumulate()
的返回值类型与第三个参数的类型一致
#include<iostream>
#include<vector>
#include<numeric>
using namespace std;
int main() {
vector<int> nums = {1, 2, 3, 4, 5};
int result = accumulate(nums.begin(), nums.end(), 0);
cout << result << endl;
return 0;
}
输出结果
15
inner_product
template <class InputIterator1, class InputIterator2, class T>
T inner_product (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, T init);
template <class InputIterator1, class InputIterator2, class T,
class BinaryOperation1, class BinaryOperation2>
T inner_product (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, T init,
BinaryOperation1 binary_op1,
BinaryOperation2 binary_op2);
//第一个op1是对init最后返回结果的操作,版本1中默认为+,第二个op2是对内积操作的自定义,版本1中默认为乘
参数 | 说明 |
---|---|
first1 | 第一个容器的某一个迭代器 |
last1 | 第一个容器的某一个迭代器,在first后面 |
first2 | 第二个容器的某一个迭代器 |
init | 初始值 |
作用:将first1到last1之间的对象(左闭右开),与first2及其对应位置的对象相乘,并且加上init
如果first2长度不够,会提前结束,first1同理
// inner_product example
#include <iostream> // std::cout
#include <functional> // std::minus, std::divides
#include <numeric> // std::inner_product
int main () {
int init = 100;
int series1[] = {10,20,30};
int series2[] = {1,2,3};
std::cout << "using default inner_product: ";
std::cout << std::inner_product(series1,series1+3,series2,init);
std::cout << '\n';
return 0;
}
输出结果
using default inner_product: 240
merge和inplace_merge
将 2 个有序序列合并为 1 个有序序列
C++ STL 标准库的开发人员考虑到用户可能需要自定义排序规则,因此为 merge()
函数设计了以下 2 种语法格式:
//以默认的升序排序作为排序规则
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result);
//以自定义的 comp 规则作为排序规则
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp);
first1
、last1
、first2
以及 last2
都为输入迭代器,[first1, last1)
和 [first2, last2) 各用来指定一个有序序列;result
为输出迭代器,用于为最终生成的新有序序列指定存储位置;comp
用于自定义排序规则。同时,该函数会返回一个输出迭代器,其指向的是新有序序列中最后一个元素之后的位置。
注意,当采用第一种语法格式时,[first1, last1)
和 [first2, last2)
指定区域内的元素必须支持 < 小于运算符;同样当采用第二种语法格式时,[first1, last1)
和 [first2, last2)
指定区域内的元素必须支持 comp
排序规则内的比较运算符。
注意,merge()
函数底层是通过拷贝的方式实现合并操作的。换句话说,上面程序在采用 merge()
函数实现合并操作的同时,并不会对 first
和 second
数组有任何影响。
当 2 个有序序列存储在同一个数组或容器中时,如果想将它们合并为 1 个有序序列,除了使用 merge()
函数,更推荐使用 inplace_merge()
函数。和 merge()
函数相比,inplace_merge()
函数的语法格式要简单很多:
//默认采用升序的排序规则
void inplace_merge (BidirectionalIterator first, BidirectionalIterator middle,
BidirectionalIterator last);
//采用自定义的 comp 排序规则
void inplace_merge (BidirectionalIterator first, BidirectionalIterator middle,
BidirectionalIterator last, Compare comp);
其中,first
、middle
和 last
都为双向迭代器,[first, middle)
和 [middle, last)
各表示一个有序序列。返回值为空。
和 merge()
函数一样,inplace_merge()
函数也要求 [first, middle)
和 [middle, last)
指定的这 2 个序列必须遵循相同的排序规则,且当采用第一种语法格式时,这 2 个序列中的元素必须支持 < 小于运算符;同样,当采用第二种语法格式时,这 2 个序列中的元素必须支持 comp
排序规则内部的比较运算符。不同之处在于,merge()
函数会将最终合并的有序序列存储在其它数组或容器中,而 inplace_merge()
函数则将最终合并的有序序列存储在 [first, last)
区域中。
min_element和max_element
min_element(iterator start, iterator end, compare comp)
iterator start, iterator end
这些是指向容器中范围的迭代器位置。compare comp
它是一个可选参数(一个函数),是比较函数(比较大小的规则)。
返回值: 它返回一个迭代器,指向给定范围内具有最小值的元素。
max_element
用法与min_element
类似
distance
distance()
函数用于计算两个迭代器表示的范围内包含元素的个数,其语法格式如下:
template<class InputIterator>
typename iterator_traits<InputIterator>::difference_type distance (InputIterator first, InputIterator last);
其中,first
和 last
都为迭代器,其类型可以是输入迭代器、前向迭代器、双向迭代器以及随机访问迭代器;该函数会返回[first, last)
范围内包含的元素的个数。
注意,first
和 last
的迭代器类型,直接决定了 distance()
函数底层的实现机制:
- 当
first
和last
为随机访问迭代器时,distance()
底层直接采用last
-first
求得[first, last)
范围内包含元素的个数,其时间复杂度为O(1)
常数阶; - 当
first
和last
为非随机访问迭代器时,distance()
底层通过不断执行++first
(或者first++
)直到first
==last
,由此来获取[first, last)
范围内包含元素的个数,其时间复杂度为O(n)
线性阶。
upper_bound和lower_bound
upper_bound
:返回序列中第一个大于查找值的位置的指针或迭代器
lower_bound
:返回序列中第一个大于等于查找值的位置的指针或迭代器
二者都需要原序列按一定规则排好序,复杂度为O(logn)
同时,该函数会返回一个正向迭代器,当查找成功时,迭代器指向找到的元素;反之,如果查找失败,迭代器的指向和 last
迭代器相同。
//#include <bits/stdc++.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int a[5]={1, 2, 3, 4, 5};
vector<int> b={1, 2, 3, 4, 5};
int main()
{
int px = upper_bound(a, a+5, 3) - a;
int x = *upper_bound(a, a+5, 3);
int py = lower_bound(b.begin(), b.end(), 3) - b.begin();
int y = *lower_bound(b.begin(), b.end(), 3);
cout << px << " " << x << endl;
cout << py << " " << y << endl;
}
输出结果:
3 4
2 3
binary_search
二分查找,如果找到返回true
,没找到就返回false
,复杂度为O(logn)
,要求序列升序
//查找 [first, last) 区域内是否包含 val
binary_search(ForwardIterator first, ForwardIterator last, val)//first 和 last 都为正向迭代器
prev_permutation和next_permutation
prev_permutation
prev_permutation()
函数功能是输出所有比当前排列小的排列,顺序是从大到小。
//#include <bits/stdc++.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int a[3]={1, 2, 3};
int b[3]={2, 1, 3 };
vector<char> c={'A','B','C'};
vector<char> d={'B','A','C'};
int main()
{
do{
cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;
}while (prev_permutation(a,a+3));
cout<<"--------"<<endl;
do{
cout<<b[0]<<" "<<b[1]<<" "<<b[2]<<endl;
}while (prev_permutation(b,b+3));
cout<<"--------"<<endl;
do{
cout<<c[0]<<" "<<c[1]<<" "<<c[2]<<endl;
}while (prev_permutation(c.begin(),c.end()));
cout<<"--------"<<endl;
do{
cout<<d[0]<<" "<<d[1]<<" "<<d[2]<<endl;
}while (prev_permutation(d.begin(),d.end()));
return 0;
}
输出结果:
1 2 3
--------
2 1 3
1 3 2
1 2 3
--------
A B C
--------
B A C
A C B
A B C
next_permutation
产生全排列,当序列存在下一个全排列时返回true
,否则返回false
,要求原序列升序
**为什么要升序:**这个函数在下一个排列大于上一个排列时返回 true
,如果上一个排列是序列中最大的,它返回 false
。
//#include <bits/stdc++.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int a[3]={1, 2, 3};
int b[3]={2, 1, 3 };
vector<char> c={'A','B','C'};
vector<char> d={'B','A','C'};
int main()
{
do{
cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;
}while (next_permutation(a,a+3));
cout<<"--------"<<endl;
do{
cout<<b[0]<<" "<<b[1]<<" "<<b[2]<<endl;
}while (next_permutation(b,b+3));
cout<<"--------"<<endl;
do{
cout<<c[0]<<" "<<c[1]<<" "<<c[2]<<endl;
}while (next_permutation(c.begin(),c.end()));
cout<<"--------"<<endl;
do{
cout<<d[0]<<" "<<d[1]<<" "<<d[2]<<endl;
}while (next_permutation(d.begin(),d.end()));
return 0;
}
输出结果:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
--------
2 1 3
2 3 1
3 1 2
3 2 1
--------
A B C
A C B
B A C
B C A
C A B
C B A
--------
B A C
B C A
C A B
C B A
对于不是一定顺序的排列,使用时将prev_permutation
和next_permutation
结合使用。