set和map的时间复杂度类似,内部实现类似,操作类似,故不详细给出具体代码,只有关键操作
1.set的构造函数
我们通常用如下方法构造一个set:
set<int>s;
2.数据插入函数
在构造set容器后,我们就可以往里面插入数据了。例如
s.insert(5);
用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当set中有这个关键字时,insert操作是插入不了数据的.
那么这就涉及到我们怎么知道insert语句是否插入成功的问题了,可以用pair来获得是否插入成功,程序如下
pair<set<int>::iterator, bool> Insert_Pair;
Insert_Pair=s.insert(1);
我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个set的迭代器,如果插入成功的话Insert_Pair.second应该是true的,否则为false。
3. set的大小
可以用size函数,用法如下:
int nSize = s.size();
4.数据的遍历
这里也提供三种方法,对set进行遍历
第一种:应用前向迭代器,下面举例说明
set<int>s;
set<int>::iterator iter;
for(iter = s.begin(); iter != s.end(); iter++)//++是logN的时间复杂度
......
第二种:应用反向迭代器,下面举例说明
set<int>s;
set<int>::reverse_iterator iter;
for(iter = s.rbegin(); iter != s.rend(); iter++)//++是logN的时间复杂度
......
5.数据的查找
这里给出两种数据查找方法
第一种:用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于set的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了
int t=s.count(1);
第二种:用find函数来定位数据出现位置,它返回一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果set中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器,程序说明
iter=s.find(t);
if(iter!=s.end())...//查找到了
6.数据的清空与判空
清空set中的数据可以用clear()函数,判定set中是否有数据可以用empty()函数,它返回true则说明是空set
if(s.empty())cout<<"Empty"<<endl;//判空
s.clear()//清空
7.数据的删除
这里要用到erase函数,它有三个重载了的函数,下面在例子中详细说明它们的用法
s.erase(iterator);//删除定位器iterator指向的键值
s.erase(iterator_first,iterator_second);//删除定位器[iterator_first,iterator_second)指向的键值
s.erase(key_value)//删除键值key_value,如果成功删除了会返回1,否则返回0
8.排序
set是用来存放< key_value >的,因此map内部就已经按照key_value来排序了。比如如果是string作为key_value,遍历就是字典序输出。
因此要求我们的key_value必须能比较大小。
以上代码在排序上是不存在任何问题的,因为上面的key_value是int型,它本身支持小于号运算。在一些特殊情况,比如key_value是一个结构体,涉及到排序就会出现问题,因为它没有小于号操作,在编译的时候过不去,重载小于号就行了。
9.集合基本运算
stl的本意,set就是一个集合,因此要可以进行集合的基本运算。
而STL中的算法中的交并集,所用到的容器不一定要是不能有重复元素.并集等的结果是排好序的一个集合.默认是通过<来比较.所以按照默认操作容器的元素必须可以进行运算符<的操作,如果是自定义类型必须重载运算符<.
int *end = set_intersection(s.begin(),s.end(),s2.begin(),s2.end(),ans,compare());//交集,ans是一个数组,存放答案,compare是比较函数,end是这个数组末尾的指针
end = set_difference(s.begin(),s.end(),s2.begin(),s2.end(),ans,compare());//s2相对于s1的差集
end = set_difference(s2.begin(),s2.end(),s.begin(),s.end(),ans,compare());//s1相对于s2的差集
end = set_union(s.begin(),s.end(),s2.begin(),s2.end(),ans,compare());//并集
end = set_symmetric_difference(s.begin(),s.end(),s2.begin(),s2.end(),ans,compare());//对称差
10.二分查找
s.lower_bound(key_value);
s.upper_bound(key_value);
这个查找的结果和lower_bound,upper_bound一样,不说了
11.求x的前驱后继
set<int>::iterator iter;
iter=s.find(x);//iter--就是前驱,iter++就是后继,注意--,++不要超出了set,也就是说iter==s.begin()时不--,++iter==s.end()的时候不要*iter。另外也可以用lower_bound,upper_bound来求x的前驱后继
iter=s.upper_bound(x);//就是x的后继
iter=s.lower_bound(x);iter--;//就是x的前驱,iter==s.begin()时不--
12.其他
还要说明的是,set中由于它内部有序,由红黑树保证,因此很多函数执行的时间复杂度都是log2N的(迭代器++也是log2N),如果用set函数可以实现的功能,而STL Algorithm也可以完成该功能,建议用set自带函数,效率高一些。
下面说下,set在空间上的特性,由于set的每个数据对应红黑树上的一个节点,这个节点在不保存你的数据时,是占用16个字节的,一个父节点指针,左右孩子指针,还有一个枚举值(标示红黑的,相当于平衡二叉树中的平衡因子),这些地方很费内存…
13.例题
题目描述
Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。
Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况:
该天的最小波动值 = min {该天以前某一天的营业额 - 该天营业额}
当最小波动值越大时,就说明营业情况越不稳定。
而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。
第一天的最小波动值为第一天的营业额。
输入
第一行为正整数n,表示该公司从成立一直到现在的天数(n<=100000)
接下来的n行每行有一个整数 ,表示第i天公司的营业额。
输出
输出文件仅有一个正整数,即每一天的最小波动值之和。答案保证在int范围内。
样例输入
6
5
1
2
5
4
6
样例输出
12
就是一个求前驱后继的题。
下面给出代码
#include <set>
#include <string>
#include <cstdio>
#include <iostream>
using namespace std;
set<int>s;
int n,ans,t;
inline int abs(int x)
{
return x>0?x:-x;
}
int main()
{
scanf("%d%d",&n,&t);
ans=t;
s.insert(t);
for(int i=1;i<n;++i)
{
int prv=999999999,nxt=999999999;
scanf("%d",&t);
pair<set<int>::iterator, bool>ins;
ins=s.insert(t);
if(!ins.second)continue;
if(ins.first!=s.begin()){ins.first--;prv=*ins.first;ins.first++;}
ins.first++;
if(ins.first!=s.end())nxt=*ins.first;
ans+=min(abs(nxt-t),abs(prv-t));
}
printf("%d\n",ans);
}