C++STL库的set就是一个二叉查找树,并且支持结构体。
在写结构体式的二叉查找树时,需要在结构体里面定义操作符 < ,因为需要比较。
set经常会用到迭代器,这里说明一下迭代器:可以类似的把它看成是一个“下标”,它是指向set集合中的某个元素的指针,它用来遍历这个集合。
头文件
#include<set>
#include<iterator> //set 经常会使用到迭代器,因此需要迭代器的头文件
定义二叉查找树:
set<数据类型> 名称:
multiset<数据类型> 名称; //multiset 是可重集合,set是不可重集合。
定义迭代器:
set<数据类型>::iterator 名称;
multiset<数据类型>::iterator 名称;
一些常用的操作:
插入一个元素v:
S.insert(v); //S是定义过的集合,下面相同
集合的大小:
S.size();
集合为空吗:
S.empty();
集合中最小的元素:
S.begin(); //这是一个指向那个位置的指针,若要值则要在前面加上*
*S.begin(); //前面加*就是那个位置的值
集合中最大的元素:
S.end(); //解释同上
*S.end();
集合中>=v的第一个元素:
it=S.lower_bound(v) //it是定义过的迭代器,注意集合不为空
集合中>v的第一个元素:
it=S.upper_bound(v)//要注意存在这样的>v的数,否则会溢出
迭代器操作:(a,b是定义过的迭代器)
a=b; //赋值操作
a++; //增加一位,即指向比这个元素a大一点点的那个元素
a--; //减少一位,即指向比这个元素a小一点点的那个元素
删除迭代器it指向的那个元素:
S.erase(it);
删除数字为v的那个元素(注意用在multiset时会删除所有v的元素):
S.erase(v);
从小到大输出所有集合中的元素:
copy(S.begin(),S.end(),ostream_iterator<数据类型>(cout," "));
以上就是一些常用的指令,下面看一道模板题。
题目: codevs 1285 宠物收养所
链接:http://codevs.cn/problem/1285/
个人觉得这道题的算法还是很巧妙的。
题目中说:同一时间呆在收养所中的,要么全是宠物,要么全是领养者。这就意味着不可能有人和宠物同时存在的情况,也就是有一个宠物那一个人必须领走。如果宠物少,那么就只能等在那里,等宠物来了。其实这道题中人领养宠物和宠物领养人是等价的,因此,在宠物集合为空的时候,把人当成宠物放进集合(二叉查找树)里面,就可以解决了。
还有一个小技巧,为了避免访问lower_bound的时候越界,可以在刚开始的时候加入正无穷和负无穷(一个允许的很大的值)两个边界,在结算的时候判断一下就可以了。还有要注意这个时候的集合为空的判断是S.size()==2。
附代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<set> 4 #include<iterator> 5 using namespace std; 6 const int maxn=80010; 7 const int INF=(1<<30); 8 const int MOD=1000000; 9 10 int n,ans,sert; 11 set<int> S; 12 set<int>::iterator lt,rt; 13 14 int main() 15 { 16 cin>>n; 17 S.insert(INF),S.insert(-INF); 18 for(int i=1,x,y;i<=n;i++) 19 { 20 cin>>x>>y; 21 if(2==S.size()) sert=x,S.insert(y); 22 else if(x==sert) S.insert(y); 23 else 24 { 25 rt=S.lower_bound(y); 26 lt=rt,lt--; 27 if(y-*lt<=*rt-y && *lt!=-INF) ans=(y-*lt+ans)%MOD,S.erase(lt); 28 else ans=(*rt-y+ans)%MOD,S.erase(rt); 29 } 30 } 31 cout<<ans<<endl; 32 return 0; 33 }