/******************************************************set和Multiset****************************************************/
使用set和multiset之前,必须先含入头文件: #include <set>
namespace std{
template<class T,class Compare = less<T>,class Allocator = allocator<T>>
class set;
template<class T,class Compare = less<T>,class Allocator = allocator<T>>
class multiset;
}
关于排序准则:
1)必须是反对称的:若x<y为真,则y<x为假
2)必须是可传递的:若x<y且y<z,则x<z
3)必须是非自反的:x<x永远为假,op(x,x)永远为假
set和multiset通常由平衡二叉树实现.
自动排序造成set和multiset的一个重要限制:你不能直接改变元素值这样会打乱原本的排序顺序。为此,要改变元素值,
必须先删除旧元素,再插入新元素。
通过迭代器进行元素间接存取,有一个限制:从迭代器角度看,元素值是常数。
set 和 multiset的构造析构函数:
set c;
set c(op);
set c1(c2);
set c(beg,end);
set c(beg,end,op);
c.~set()
set可为以下形式:
set/multiset<Elem>
set/multiset<Elem,op>
有两种方式可以定义排序准则(后有例子):
1)以template参数定义:
std::set<int,std::greater<int>> coll; 这种情况下,排序准则就是型别的一部分,只有排序准则相同的容器才能被合并
2)以构造函数参数定义:
typedef set<int,RuntimeCmp<int>> IntSet;
RuntimeCmp<int> reverse_order(RuntimeCmp<int>::reverse);
IntSet coll2(reverse_order);
如果执行期才获得排序准则,且需要用到不同的排序准则(但数据型别必须相同),此方式可派上用场。
元素比较动作只能用于型别相同的容器,换言之,元素和排序准则都必须有相同的型别,否则编译出错。如果要比较拥有不同排序准则的容器,必须采用比较算法。
c.size() c.empty() c.max_size() == != < > <= >=
set和multiset的搜寻操作函数:
赋值操作的两端容器必须具有相同型别。“比较准则”本身可以不同,但其型别必须相同,如果准则不同,准则本身也会被赋值/交换count(elem)
find(elem) // 找第一个,找不到就返回end()
lower_bound(elem)
upper_bound(elem)
equal_range(elem)
c1 = c2; c1.swap(c2); swap(c1,c2);
如果要移除set/multiset的元素,只能使用它们所提供的成员函数:
c.insert(elem) // 返回新元素位置
c.insert(pos,elem) //返回新元素位置
c.insert(beg,end) //void
c.erase(elem) //返回被移除的元素个数
c.erase(pos) //void ,vector返回的是下一个元素位置,这种差别完全是为了性能
c.erase(beg,end) // void
c.clear()
//无 remove
multiset允许元素重复,而set不允许。因此将某元素安插至一个set内,而该set已含同值元素,则安插失败,所以set的返回型别
是以pair组织起来的两个值:pair<iterator,bool>,first 返回新元素的位置,second表示是否安插成功
set和multiset运用实例:
#include <iostream>
#include <set>
#include <iterator>
#include <algorithm>
using namespace std;
int main(){
typedef set<int,greater<int>> IntSet;
IntSet coll1;
coll1.insert(4);
coll1.insert(3);
coll1.insert(5);
coll1.insert(1);
coll1.insert(6);
coll1.insert(2);
coll1.insert(5); // 会被程序忽略
IntSet::iterator pos;
for(pos = coll1.begin();pos != coll1.end; ++pos){
cout<<*pos<" ";
}
cout<<endl;
pair<IntSet::iterator,bool> status = coll1.insert(4);
if(status.second){
cout<<"4 inserted as element "
<<distance(coll.begin(),status.first) + 1
<<endl;
}else{
cout<<"4 already exists"<<endl;
}}
set<int> coll2(coll1.begin(),coll1.end());
copy(coll2.begin(),coll2.end(),ostream_iterator<int>(cout," "));
cout<<endl;
coll2.erase(coll2.begin(),coll2.find(3));
int num;
num = coll2.erase(5);
cout<<num<< " element(s) removed"<<endl;
copy(coll2.begin(),coll2.end(),ostream_iterator<int>(cout," "));
cout<<endl;
执行期指定排序准则:
#include <iostream>
#include <set>
#include "print.hpp"
using namespace std;
template<class T>
class RuntimeCmp{
public:
enum cmp_mode{normal,reverse};
private:
cmp_mode mode;
public:
RuntimeCmp(cmp_mode m=normal) : mode(m) {
}
bool operator()(const T& t1,const T& t2) const{
return mode = normal ? t1 < t2 : t2 < t1;
}
bool operator == (const RuntimeCmp& rc){
return mode == rc.mode;
}
};
typedef set<int,RuntimeCmp<int>> IntSet;
void fill(IntSet& set);
int main(){
IntSet coll1;
fill(coll1);
PRINT_ELEMENTS(coll1,"coll1: ");
RuntimeCmp<int> reverse_order(RuntimeCmp<int>::reverse);
IntSet coll2(reverse_order);
fill(coll2);
PRINT_ELEMENTS(coll2,"coll2: ");
coll1 = coll2;
coll1.insert(3);
PRINT_ELEMENTS(coll1,"coll1: ");
}
void fill(IntSet& set){
set.insert(4);
set.insert(7);
set.insert(5);
set.insert(1);
set.insert(6);
set.insert(2);
set.insert(5);
}
/******************************************************map和multimap****************************************************/
使用map和multimap之前,必须包含头文件: #include <map>
namespace std{
template<class key,class T,
class Compare = less<key>,
class Allocator = allocator<pair<const key,T>>>
class map;
template<class key,class T,
class Compare = less<key>,
class Allocator = allocator<pair<const key,T>>>
class multimap;
}
注:key/value 必须具备可复制,可赋值性质;对排序准则而言,key必须是可比较的。map可作为关联式数组使用。
不可以直接更改元素的key,当value是非常数时,value可更改。若一定要改变元素的key,只有一条路:以一个value相同的
新元素替换掉旧元素。map提供了一种非常方便的手法让你改变元素的key:
coll["new_key"] = coll["old_key"];
coll.erase("old_key");
m[key]返回一个reference,指向键值为key的元素,如果该元素尚未存在,就安插该元素。
std::cout<<coll["otto"]; // 若该键值不存在,就安插,然后打印其值,缺省情况下是0,此安插速度慢,先构造再赋值。
map/multimap绝大多数操作都与set/multiset相同,此处略过。
成员函数find用来查找拥有某个key值的第一个元素,并返回一个指向该位置的迭代器或end()。你不能用find来查找拥有某特定
value的元素,你必须用find_if,或干脆写一个显式循环:
std::multimap<std::string,float> coll;
......
std::multimap<std::string,float>::iterator pos;
for(pos = coll.begin();pos != coll.end(); ++pos){
if(pos->second == value){
do_something();
}
}
有三种不同方法可以将value传入map:
1)运用value_type:value_type是容器本身提供的型别定义。
std::map<std::string,float> coll;
coll.insert(std::map<std::string,float>::value_type("otto",22.3));
2)运用pair<>
coll.insert(std::pair<std::string,float>("otto",22.3));
coll.insert(std::pair<const std::string,float>("otto",22.3));
第一个型别并不正确,但会被转换成真正的元素型别,因为insert成员函数被定义为member template
3)运用make_pair():最简单,这个函数根据传入的参数构造一个pair对象
coll.insert(std::make_pair("otto",22.3)); // member template insert() 执行必要的型别转换
eg: std::map<std::string,float> coll;
if(coll.insert(std::make_pair("otto",22.3)).second){ //对于insert(value),返回pair<iterator,bool>
std::cout<<"ok can insert"<<std::endl;
}else{
std::cout<<"Oops,could not insert,key already exists"<<std::endl;
}
如果multimap内含重复元素,你不能使用erase()来删除重复元素中的第一个,你可以用成员函数find:
typedef std::multimap<std::string,float> StringFloatMMap;
StringFloatMMap coll;
StringFloatMMap::iterator pos;
pos = coll.find(key);
if(pos != coll.end()){
coll.erase(pos);
}
当移除迭代器所指对象时,有一个很大的危险:
for(pos = coll.begin();pos != coll.end(); ++pos){
if(pos->second == value){
coll.erase(pos); // Error!
}
对pos所指元素实施erase(),会使pos不再成为一个有效的coll迭代器,此后未对pos重新设值就径行使用pos,前途未卜!
应该这样:
for(pos = coll.begin();pos != coll.end(); ){
if(pos->second == value){
coll.erase(pos++);
}else{
++pos;
}
}
map和multimap运用实例:
将map当做关联式数组:
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main(){
typedef map<string,float> StringFloatMap;
StringFloatMap stocks;
stocks["BASF"] = 369.50;
stocks["VW"] = 413.50;
stocks["Daimler"] = 819.00;
stocks["BWM"] = 822.00;
stocks["Siements"] = 834.20;
StringFloatMap::iterator pos;
for(pos = stocks.begin(); pos != stocks.end(); ++pos){
cout<<"stock: "<<pos->first<<"\t"
<<"price: "<<pos->second<<endl;
}
cout<<endl;
for(pos = stocks.begin(); pos != stocks.end(); ++pos){
pos->second *=2;
}
for(pos = stocks.begin(); pos != stocks.end(); ++pos){
cout<<"stock: "<<pos->first<<"\t"
<<"price: "<<pos->second<<endl;
}
cout<<endl;
stocks["Volkswagen"] = stocks["VM"];
stocks.erase["VM"];
for(pos = stocks.begin(); pos != stocks.end(); ++pos){
cout<<"stock: "<<pos->first<<"\t"
<<"price: "<<pos->second<<endl;
}
}
将multimap当做字典:
#include <iostream>
#include <map>
#include <string>
#include <iomanip>
using namespace std;
int main(){
typedef multimap<string,string> StrStrMMap;
StrStrMMap dict;
dict.insert(make_pair("day","Tag"));
dict.insert(make_pair("strange","fremd"));
dict.insert(make_pair("car","Auto"));
dict.insert(make_pair("smart","elegant"));
dict.insert(make_pair("trait","Merkmal"));
dict.insert(make_pair("strance","seltsam"));
dict.insert(make_pair("smart","raffiniert"));
dict.insert(make_pair("smart","klug"));
dict.insert(make_pair("clever","raffiniert"));
StrStrMMap::iterator pos;
cout.setf(ios::left,ios::adjustfield);
cout<<' '<<setw(10)<<"english"<<"german "<<endl;
cout<<setfill('-')<<setw(20)<<""<<setfill(' ')<<endl;
for(pos = dict.begin(); pos != dict.end(); ++pos){
cout<<' '<<setw(10)<<pos->first.c_str()
<<pos->second<<endl;
}
cout<<endl;
string world("smart");
cout<<word<<": "<<endl;
for(pos = dict.lower_bound(word); pos != dict.upper_bound(word); ++pos){
cout<<" "<<pos->second<<endl;
}
word = ("raffiniert");
for(pos = dict.begin(); pos != dict.end(); ++pos){
if(pos->second ==word){
cout<<" "<<pos->first<<endl;
}
}}
用find_if算法搜寻具有某特定值的元素:
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
template<class K,class V>
class value_equals{
private:
V value;
public:
value_equals(const V& v) : value(v){ }
bool operator()(pair<const K,V>elem){
return elem.second == value;
}
};
int main(){
typedef map<float,float> FloatFloatMap;
FloatFloatMap coll;
FloatFloatMap::iterator pos;
coll[1] = 7;
coll[2] = 4;
coll[3] = 6;
coll[4] = 3;
coll[5] = 2;
coll[6] = 1;
coll[7] = 3;
pos = coll.find(3.0);
if(pos != coll.end()){
cout<<pos->first<<": "
<<pos->second<<endl;
}
pos = find_if(coll.begin(),coll.end(),value_equals<float,float>(3.0));
if(pos != coll.end()){
cout<<pos->first<<": "
<<pos->second<<endl;
}
}
综合实例:运用map,multimap并于执行期指定排序准则
#include <iostream>
#include <iomanip>
#incldue <map>
#include <string>
#include <algorithm>
using namespace std;
class RuntimeStringCmp{
public:
enum cmp_mode{normal,nocase};
private:
const cmp_mode mode;
static bool nocase_compare(char c1,char c2){
return toupper(c1)<toupper(c2);
}
public:
RuntimeStringCmp(cmp_mode m = normal) : mode(m){ }
bool operator()(const string& s1,const string& s2) const {
if(mode == normal){
return s1 < s2 ;
}else{
return lexicographical_compare(s1.begin(),s1.end(),s2.begin(),s2.end(),nocase_compare);
};}
}
typedef map<string,string,RuntimeStringCmp> StringStringMap;
void fillAndPrint(StringStringMap& coll);
int main(){
StringStringMap coll1;
fillAndPrint(coll1);
StringStringMap ignorecase(StringStringMap::nocase);
StringStringMap coll2(ignorecase);
fillAndPrint(coll2);
}
void fillAndPrint(StringStringMap& coll){
coll["deutsch"] = "German";
......
StringStringMap::iterator pos;
cout.setf(ios::left,ios::adjustfield);
for(pos = coll.begin(); pos != coll.end(); ++pos){
cout<<setw(15)<<pos->first.c_str()<<" "
<<pos->second<<endl;
}
cout<<endl;
}